Python面向对象进阶:__slots__、@property、枚举类、元类到底是什么?一篇彻底讲明白!(小白必看) (六.2)
2026/7/4 3:31:04 网站建设 项目流程

一、多重继承:一个类为什么可以有多个"爸爸"?

上一篇文章,我们学习了继承。

例如:

class Animal: def eat(self): print("动物都会吃东西")

创建狗类:

class Dog(Animal): pass

这时候:

dog = Dog() dog.eat()

输出:

动物都会吃东西

是不是很简单?Dog 继承了 Animal,所以自动拥有了eat()方法,这种继承方式叫做:

单继承

也就是说:

一个子类只有一个父类。

关系可以画成这样:

Animal │ │ Dog

1.1 那如果一个类想同时拥有两种能力怎么办?

来看一个例子: 假设现在开发一款游戏,游戏里有很多角色,有的角色会跑:

class Run: def run(self): print("我会跑")

有的角色会飞:

class Fly: def fly(self): print("我会飞")

现在我们想创建一个超级英雄:他既会跑又会飞,怎么办?如果按照以前的方法,是不是得重新写一遍?

class SuperMan: def run(self): print("我会跑") def fly(self): print("我会飞")

当然可以。但是这样做有一个问题,如果以后:

会游泳 会隐身 会发射激光 会瞬移

每增加一种能力了,都得重新复制代码。

程序员最讨厌的就是:

复制、粘贴、复制、粘贴……

因为复制的时候很开心,后面改代码的时候就会发现:

哪里都要改,非常痛苦!!

1.2 Python 提供了多重继承

Python 可以同时继承多个父类。

例如:

class Run: def run(self): print("我会跑") class Fly: def fly(self): print("我会飞") class SuperMan(Run, Fly): pass

创建对象:

hero = SuperMan() hero.run() hero.fly()

输出:

我会跑 我会飞

有没有发现?SuperMan并没有写:

run() fly()

但是却可以直接调用,因为它同时继承了:

  • Run
  • Fly

所以两个类的方法都拥有了。

1.3 为什么叫"多重继承"?

因为,以前:

一个爸爸 │ ▼ 儿子

现在变成了:

Run Fly │ │ \ / \ / SuperMan

一个类可以同时学习多个父类的能力,这就是:

多重继承。

1.4 多重继承什么时候会用到?

举一个现实中的例子:假设开发一个机器人。

机器人,可以说话:

class Speak:

可以跳舞:

class Dance:

可以拍照:

class Camera:

如果需要"会说话还能拍照" 的机器人。直接:

class Robot(Speak, Camera):

是不是比重新写一遍方便得多?所以:

多重继承最大的作用就是复用代码。

1.5 多重继承有没有缺点?

当然有,假设两个父类都有同一个方法:

class A: def hello(self): print("A")
class B: def hello(self): print("B")

子类:

class C(A, B): pass

执行:

c = C() c.hello()

输出:

A

为什么不是 B?因为 Python 有自己的查找顺序,它会按照一定规则寻找方法。

对于初学者来说,不用去死记这个规则,只需要知道:

如果多个父类有同名方法,Python 会按照继承顺序查找。

所以实际开发中不要随便使用多重继承,否则后期自己都会绕晕。

1.6 Python 为什么还能支持多重继承?

很多编程语言,例如 Java并不支持类的多重继承。为什么?因为继承关系太复杂,容易产生歧义。

Python 之所以支持,是因为它设计了一套完整的方法查找规则(MRO),不过这属于比较深入的知识。对于初学者来说,暂时知道:

Python 可以支持多个父类即可。

以后学习框架的时候,自然会接触到。

本节小结

多重继承就是:

一个类,同时继承多个父类。

它最大的优点:

✅ 复用代码。

✅ 一个类拥有多个能力。

但也不要滥用,因为继承关系太复杂,维护起来会越来越困难。

对于初学者来说,记住一句话:

能用单继承解决,就不要为了炫技写多重继承。

二、定制类:让自己的对象变得和 Python 内置对象一样聪明

不知道大家有没有想过一个问题。为什么列表可以这样写:

nums = [1, 2, 3] print(len(nums))

字符串也可以:

print(len("Python"))

甚至字典:

data = {"name": "张三"} print(len(data))

为什么它们都支持:

len()

再比如,打印对象:

print(nums)

输出:

[1, 2, 3]

为什么打印自己写的类:

print(student)

却变成:

<__main__.Student object at 0x000001F4...>

这到底是为什么,答案就是:

Python 内置对象实现了很多特殊方法。

如果我们自己也实现这些方法,自己的对象也会拥有这些能力。

这就是:

定制类。

简单来说:

按照 Python 的规则,给自己的类增加特殊能力。

2.1 第一个魔法方法:__str__()

先来看一个例子。

class Student: def __init__(self, name): self.name = name

创建对象:

s = Student("张三") print(s)

输出:

<__main__.Student object at 0x000001F4...>

很多小伙伴第一次看到这里都会说:

这是什么乱码?

其实这不是乱码,这是 Python 默认打印出来的对象信息。对于程序来说没有问题,但是对于人来说,几乎没有任何意义。

2.2 怎么让它变好看?

只需要重写:

__str__()

例如:

class Student: def __init__(self, name): self.name = name def __str__(self): return f"学生姓名:{self.name}"

再次打印:

s = Student("张三") print(s)

输出:

学生姓名:张三

是不是一下舒服多了?以后只要:

print(对象)

Python 就会自动调用:

__str__()

2.3 __repr__()又是什么?

很多同学会发现,网上还有:

__repr__()

它和:

__str__()

长得几乎一样,有什么区别?

简单来说:

  • __str__():给用户看的。
  • __repr__():给程序员看的。

对于初学者来说,很多时候可以直接让:

__repr__ = __str__

这样两个效果一样,等以后接触大型项目再深入学习即可。

2.3 __len__():让对象支持 len()

大家都知道:

len([1,2,3])

输出:

3

其实,Python 背后调用的是:

__len__()

所以我们自己的对象也可以。

class Student: def __len__(self): return 10

创建对象:

s = Student() print(len(s))

输出:

10

是不是很神奇?以后只要别人调用:

len(对象)

Python 就会自动执行:

__len__()

2.4 __call__():对象也能像函数一样调用

函数:

print()

可以直接:

print()

但是对象呢?正常情况下:

s()

会报错,如果实现:

class Student: def __call__(self): print("对象被调用了")

那么:

s = Student() s()

输出:

对象被调用了

是不是很神奇?对象居然可以像函数一样调用。

2.5 定制类到底是在定制什么?

很多小白看到这里可能会觉得:魔法方法越来越多。

其实不用紧张,它们本质上都是同一个思想:

告诉 Python:"如果别人这样操作我的对象,你应该执行什么代码。"

例如:

  • print(对象)→ 调用__str__()
  • len(对象)→ 调用__len__()
  • 对象()→ 调用__call__()

这就是所谓的定制类,你不是在学习一堆新的语法,而是在学习如何让自己的对象,拥有和 Python 内置对象一样的能力。

本节小结

定制类并不是一个新的类,而是通过实现一些魔法方法,让自己的对象支持更多操作。

本节我们认识了几个最常用的方法:

魔法方法作用
__str__()自定义打印对象时显示的内容
__repr__()对象的开发者表示形式
__len__()让对象支持len()
__call__()让对象可以像函数一样调用

对于初学者来说,不需要把所有魔法方法都记住,记住一句话就够了:

Python 很多看似神奇的功能,背后其实都是这些魔法方法在默默工作。

三、使用枚举类:固定的数据,别再用字符串乱写了

相信大家平时写代码的时候,经常会这样定义一些数据。

例如表示性别:

gender = "男"

或者:

gender = "女"

看起来没有任何问题。再比如表示订单状态:

status = "待付款"

付款之后:

status = "已付款"

发货以后:

status = "已发货"

最后:

status = "已完成"

很多刚学习 Python 的同学都会这样写。因为简单直接,容易理解。但是,如果项目越来越大,就会慢慢出现问题。

3.1 字符串真的安全吗?

来看一个例子,假设我们判断订单是否已经付款。

status = "已付款" if status == "已付款": print("开始发货")

没有问题,但是如果某一天,你不小心写成了:

status = "以付款"

注意这里不是:

已付款

而是:

以付款

只差了一个字,程序不会报错,但是:

if status == "已付款":

条件永远不成立,程序也不会提醒你。因为对于 Python 来说,它只是一个普通字符串,是真是假它不知道。

3.2 如果一个项目有几百个字符串呢?

例如,订单状态:

待付款 已付款 已发货 运输中 已签收 已完成

如果每个人都自己写,可能出现:

已付款 付款成功 已经付款 支付成功

虽然表达的是同一个意思,但是程序根本不知道,这样就很容易产生 Bug。

怎么办?Python 提供了一个更加规范的方法:

枚举类(Enum)

3.3 什么是枚举类?

一句话理解:

把固定不变的选项,统一管理起来。

例如:

一年有四季。

交通灯只有三种颜色。

一周只有七天。

订单状态只有几个固定值。

这些都是:

枚举。

也就是说,数量固定,不会随便增加。

3.4 创建一个枚举类

Python 内置了:

Enum

我们先导入:

from enum import Enum

创建一个性别枚举:

from enum import Enum Gender = Enum("Gender", ("男", "女"))

现在我们不能随便写字符串了,而是这样使用:

print(Gender.男)

输出:

Gender.男

如果获取值:

print(Gender.男.value)

输出:

1

再看:

print(Gender.女.value)

输出:

2

Python 会自动给每一个成员分配一个编号。当然,编号一般不是重点。

真正重要的是:

以后所有地方都统一使用Gender.男Gender.女,而不是随便写字符串。

3.5 为什么要用枚举?

还是拿交通灯举例,以前:

light = "红灯"

现在:

Light.RED

看到这里很多同学会觉得,"写字符串不是更简单吗?"刚开始确实如此,但是如果一个项目有几十万行代码,到处都是:

红灯 红色 停止 stop

你根本不知道哪个才是正确的,而枚举只有一种写法,大家统一使用,代码会更加规范。

3.6 实际开发中哪些地方经常使用枚举?

枚举在项目中非常常见。例如,订单状态:

待付款 已付款 已发货 已完成

用户身份:

管理员 普通用户 游客

星期:

星期一 星期二 …… 星期日

交通灯:

红灯 黄灯 绿灯

游戏角色职业:

战士 法师 射手 辅助

这些固定的数据都非常适合使用枚举类。

本节小结

枚举类就是:

把固定的数据统一管理起来。

它最大的优点就是:

  • 不容易写错。
  • 代码更加规范。
  • 大型项目更容易维护。

以后只要看到:

Enum

你就可以理解成:

这是一个固定选项的集合。

四、元类:类到底是谁创建出来的?

终于来到第六章最后一个知识点,也是很多人觉得最神秘的一个知识点。

元类(Metaclass)

网上很多教程一上来就是:

class MyMeta(type):

相信很多同学看到这里,直接关闭网页。因为根本不知道在写什么。其实,元类并没有那么可怕。

我们先回顾一下之前学习过的内容。

4.1 第一步:对象是谁创建的?

上一篇文章,我们一直在写:

class Student: pass

创建对象:

s = Student()

这里:

Student

就是一个类,而:

s

就是对象,所以对象是谁创建出来的?答案当然是:

类。

关系如下:

类 │ │ 创建 ▼ 对象

这一点大家应该都能理解。

4.2 第二步:那类是谁创建出来的?

很多同学可能会回答:

Python 创建的。

回答没错。但是更准确一点,应该是:

元类创建的。

也就是说,实际上还有这样一层关系:

元类 │ │ 创建 ▼ 类 │ │ 创建 ▼ 对象

是不是一下子清楚多了?

4.3 元类可以理解成"造类的工厂"

举个生活中的例子。

假设一家汽车工厂,它的流程是这样的:

第一步:工程师设计图纸。

第二步:工厂根据图纸生产汽车。

关系如下:

图纸 │ ▼ 汽车

Python 也类似,不过多了一层。

元类 │ ▼ 类 │ ▼ 对象

如果把对象比作汽车,类就是汽车图纸。元类就是制造图纸的工厂。

所以很多人说:

元类就是创建类的类。

虽然听起来有点绕,但是意思就是:

专门负责创建类。

4.4 那我们什么时候会写元类?

答案是:几乎不会。

是不是有点意外?事实上,对于绝大多数 Python 开发者来说。

工作几年,可能都不会自己写一个元类。因为Python 已经帮我们做好了。

例如,当我们写:

class Student: pass

实际上。

Python 背后已经使用默认元类:

type

帮我们创建好了这个类。所以我们平时根本感觉不到它的存在。

4.5 那为什么还要学习元类?

因为很多优秀的 Python 框架都会使用元类。

例如:

  • Django
  • SQLAlchemy
  • Django ORM
  • 一些大型框架

虽然你不会自己写,但是以后阅读源码的时候经常会遇到:

metaclass=

如果不知道元类是什么就会一脸懵,所以学习元类主要目的是:

知道它是什么。

而不是:

必须会写。

本节小结

元类只有一句话需要记住:

对象是类创建的,类是元类创建的。

对于初学者来说知道这一点就已经足够了,等以后学习框架源码再深入学习也不迟。

全文总结

恭喜你!如果能够坚持看到这里,说明你已经掌握了 Python 面向对象编程的大部分进阶知识。

这次我们学习了六个重要知识点。

__slots__

限制对象可以拥有的属性,让代码更加规范,还能节省一定内存。

@property

把方法变成属性,既保证数据安全,又让代码更加优雅。这是实际开发中使用频率非常高的一个知识点。

多重继承

一个类可以同时继承多个父类,实现代码复用,不过不要为了炫技而滥用。

定制类

通过实现魔法方法,让自己的对象支持:

  • print()
  • len()
  • 对象()

等各种操作。让对象拥有和 Python 内置对象一样的能力。

枚举类

适合管理固定的数据。

例如:

  • 星期
  • 性别
  • 订单状态
  • 用户角色

让代码更加规范,减少因为字符串写错而导致的 Bug。

元类

知道即可,记住一句话:

元类创建类,类创建对象。

以后学习 Django、Flask、ORM 等框架时,你还会再次遇到它。

不知不觉,我们已经从最开始的变量、列表、字典,一路学习到了 Python 面向对象编程的进阶内容。你可能会发现,现在学的很多知识,平时写几十行小程序未必都能用到。但它们存在的意义,并不是为了让代码变得更复杂,而是为了让程序在规模越来越大、参与开发的人越来越多时,依然能够保持清晰、规范、易维护。学习编程就像搭积木,基础知识决定你能不能把积木搭起来,而这些进阶知识,则决定你的积木能搭多高、搭得稳不稳。

希望通过这一系列文章,你不仅学会了 Python 的语法,更能够逐渐理解 Python 的编程思想。当以后再次看到@property__slots__Enum或者元类时,不会再觉得陌生,而是能够明白它们为什么会存在,又该在什么场景下使用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询