首页 论坛 置顶 深入浅出:一文搞懂 Python 继承、MRO 与元类

正在查看 1 个帖子:1-1 (共 1 个帖子)
  • 作者
    帖子
  • #25910

    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= 作为类型类检测的“入口点”,并且只能有一个声明。
正在查看 1 个帖子:1-1 (共 1 个帖子)
  • 哎呀,回复话题必需登录。