深入浅出:一文搞懂 Python 继承、MRO 与元类

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= 作为类型类检测的“入口点”,并且只能有一个声明。

更多