You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

元类中__new__与__init__的参数及调用顺序疑问

元类中__new__与__init__的调用逻辑及参数问题解析

一、为什么__init__的name参数还是原始值?

先直接点破核心:Python在调用元类的__init__时,传入的是类定义时的原始参数,而不是__new__方法修改后的值。

我们拆解一下你的代码执行流程:

  1. 当你定义class A(metaclass=AT): pass时,Python会先收集三个核心参数:
    • name='A'(类名)
    • bases=()(基类元组)
    • dct={}(类命名空间的字典)
  2. 调用元类的__new__方法:AT.__new__(AT, 'A', (), {})。你在这个方法里修改了name(追加HELLO)和bases(添加list作为基类),然后调用super().__new__创建并返回了一个新的类对象——这个对象的__name__被设为了'AHELLO'。
  3. 接下来Python会自动调用元类的__init__方法,但注意:它传入的参数是原始的name='A'、bases=()、dct={},再加上刚才__new__返回的类对象(也就是cls参数)。

这就是为什么你看到__init__里打印的name是A,但A.__name__是AHELLO——__init__接收的是定义时的原始参数,而__new__修改的是返回的类对象本身的属性。

另外纠正你的一个误解:__init__不是由super().__new__调用的,而是Python解释器在__new__返回类对象后自动触发的。只要__new__返回的是当前元类(或其子类)的实例,Python就会调用__init__;只有当__new__返回一个已经存在的类对象时,__init__才会被跳过。

二、为什么处理bases时__new__和__init__都要加转换代码?

同样的逻辑:__init__接收的bases是类定义时的原始值,不是__new__里修改后的版本。

比如你如果动态创建类时传入了单个基类(而非元组),__new__里收到的bases是那个单个类,你转成元组后传给super().__new__,用来创建类的基类会是正确的元组格式,但__init__收到的bases还是原始的单个类。如果你的__init__方法里有需要处理bases的逻辑(比如遍历基类、检查基类属性等),就必须再次转换,因为__init__的bases参数是未处理的原始值。

优化方案:避免重复处理

如果你不想在__new__和__init__里重复写转换代码,可以在__new__里把处理后的bases存储到类对象的一个自定义属性里,然后__init__直接用这个属性:

class AT(type):
    def __new__(mcs, name, bases, dct):
        # 统一处理bases格式
        if not isinstance(bases, tuple):
            bases = (bases,)
        # 创建类对象并存储处理后的bases
        new_cls = super().__new__(mcs, name, bases, dct)
        new_cls._processed_bases = bases
        return new_cls
    def __init__(cls, name, bases, dct):
        # 直接使用预处理后的bases,无需重复转换
        print(f"Processed bases: {cls._processed_bases}")
        pass

总结一下元类的核心调用逻辑

  • __new__:创建类对象的工厂方法,返回的是最终的类实例,可以修改类的属性(如__name__、__bases__)或调整创建参数。
  • __init__:初始化已创建的类对象,接收的是类定义时的原始参数,而非__new__修改后的参数。
  • 执行顺序:收集原始参数 → 调用__new__创建类 → 若返回的是当前元类实例,调用__init__初始化。

内容的提问来源于stack exchange,提问作者Cedric H.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:09:18