首页 › 论坛 › 置顶 › 深入浅出:一文搞懂 Python 继承、MRO 与元类
-
作者帖子
-
2025-09-04 11:08 #25910Q QPY课程团队管理员
1. 基本类
在Python中,类只是创建对象的模板。
class Animal: def speak(self): return "some sound" a = Animal() print(a.speak()) # "some sound"
2.继承(重用/扩展类)
继承意味着一个类重用或扩展另一个类。
class Dog(Animal): # Dog继承自Animal def speak(self): return "woof!"
Dog从Animal那里获取所有内容。
如果你不重写,它就会使用父类的方法。
如果你重写(def speak),则使用你的新版本。
d = Dog() print(d.speak()) # "woof!"
🔹 多重继承
Python 允许一个类从多个类继承。
class Walker: def move(self): return "walking" class Swimmer: def move(self): return "swimming" class Duck(Walker, Swimmer): pass
那么,Duck 使用哪个 move() 方法呢?
Python 使用方法解析顺序(MRO)来解决这个问题 → 一种确定性的查找顺序。duck = Duck() print(duck.move()) # "walking",因为 Walker 被列在前面 print(Duck.__mro__) # (<class '__main__.Duck'>, <class '__main__.Walker'>, <class '__main__.Swimmer'>, <class 'object'>)
3. 构造函数 (__init__)
当你执行 `obj = ClassName(…)` 时,Python 会调用该类的 `__init__` 方法。
父类的构造函数可以通过 `super()` 调用。
class Animal: def __init__(self, name): self.name = name class Dog(Animal): def __init__(self, name, breed): super().__init__(name) # 调用父类的 __init__ self.breed = breed d = Dog("Rex", "Labrador") print(d.name, d.breed) # Rex Labrador
4. 元类(类的类)
现在进入棘手的部分:
在 Python 中,一切都是对象,甚至类本身。
如果对象是由类创建的……那么类又是由什么创建的呢?
→ 答案是:元类。默认情况下,Python 使用 `type` 作为元类。
# "Animal" 本身是由 "type" 创建的对象 print(type(Animal)) # <class 'type'>
所以:
普通对象是类的实例。
类是元类的实例(通常是 `type`)。
🔹 自定义元类
您可以定义自己的 metaclass 来控制类的构建方式。
class Meta(type): # 继承自 'type' def __new__(cls, name, bases, dct): print(f"创建类 {name}") return super().__new__(cls, name, bases, dct) class MyClass(metaclass=Meta): pass
输出:
创建类 MyClass
在这里,Meta.__new__ 在类定义时运行,而不是在实例化时运行。Metaclass 就像是类的蓝图。
5. 何时使用继承与 Metaclass
继承 = 在对象之间组织和重用代码。
Metaclass = 控制或修改类本身的行为(罕见,进阶用例)。
示例 metaclass 使用:
强制编码标准(例如,所有方法必须为小写)。
自动注册类到插件系统中。
自动向类添加方法/属性。
6. 总结
继承:
在类之间重用代码。
支持多个父类。
遵循 MRO 进行查找。
构造函数(__init__):
定义类实例接受的参数。
super() 允许链式调用父类构造函数。
Metaclass:
类是对象,由 metaclass 创建(默认是 type)。
自定义 metaclass 允许您拦截或修改类的创建。
功能强大,但应用场景较窄 — 通常在构建框架/库时才需要。
对于 super() 和 metaclass 的使用我不是很确定,所以我继续研究:
1. type 作为“类的元类” 在 Python 中: 通常,当你调用一个类,比如 Dog(“Rex”),发生的事情并不是简单的函数调用。 在内部,Python 做了如下操作: obj = Dog.__call__(*args, **kwargs) 但 Dog.__call__ 来自它的元类(默认是 type)。 所以整个流程是:
Dog(...) → Dog.__class__ 是 type → type.__call__(Dog, *args, **kwargs) → Dog.__new__(Dog, *args, **kwargs) # 分配内存 → Dog.__init__(self, *args, **kwargs) # 初始化字段 → 返回实例
2. 那么 super() 实际上做了什么?super() 并不意味着“父类”。它的意思是: “在我的当前类之后,去 MRO 中的下一个类。”
示例:class A: def greet(self): print("A") class B(A): def greet(self):
print("B") super().greet() class C(A): def greet(self): print("C") super().greet() class D(B, C): def greet(self): print("D") super().greet() MRO: print(D.__mro__) # (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>) 执行: D().greet() # D # B # C # A 注意,super() 遵循了 MRO,而不仅仅是“父类”。
3. 元类的实际应用如果你给一个类指定 metaclass=Meta,那么类对象本身的创建就会被拦截。 class Meta(type): def __call__(cls, *args, **kwargs): print(f"正在创建 {cls.__name__} 的实例") return super().__call__(*args, **kwargs) class MyClass(metaclass=Meta): def __init__(self, x): self.x = x 现在: obj = MyClass(42) 执行顺序: Meta.__call__(MyClass, 42) 被调用 内部,super().__call__ 委托给 type.__call__ 然后调用 MyClass.__new__ 接着调用 MyClass.__init__
4. 为什么看起来“硬编码”是的——你必须声明 metaclass=Meta,因为只有一个元类控制类的创建。 但请注意: class Foo: pass → 自动使用 type。 class Foo(metaclass=Meta): pass → 使用你的 Meta。 由于元类本身是 type 的子类,你可以扩展 type.__call__、type.__new__ 等的逻辑。
5. 类比可以将其视为一个工厂系统: 对象 = 产品(你实际使用的实例)。 类 = 工厂(产品的蓝图)。 元类 = 工厂设计师(工厂的蓝图)。
当你执行以下代码时:
p = Product()
这就像是:
调用工厂设计者(元类)来创建一个工厂(类)→ 这在定义时只发生一次。
调用工厂(类)来生成一个产品(对象)→ 每次实例化时都会发生。
我希望你也学到了些什么。
- 使用 Class.mro 来遍历继承关系。
- 如果在本地未解决,则解析导入以解决基类。
- 一旦你有了类定义,检查 init / new 的签名 → 收集它们的参数要求。
- 检测 metaclass= 作为类型类检测的“入口点”,并且只能有一个声明。
-
作者帖子
- 哎呀,回复话题必需登录。