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

如何在Python元类中拆分__call__为__new__和__init__单独调用

Python元类中手动拆分__call__以单独控制__new__和__init__

我正在为单例场景编写Python元类,需要单独控制单例类(元类SingletonMetaMeta的实例)的__new__和__init__方法,而不是通过默认的__call__将二者一同调用。当前代码通过super().__call__完成实例创建,希望拆分为手动调用__new__和__init__,但不清楚默认__call__的参数传递规则,手动尝试时出现错误。

原始代码示例

class SingletonMetaMeta(type):
    def __new__(mcs, *args, **kwargs):  # args passed on import time with class Singleton(metaclass=NonInheritableSingletonMetaMeta)
        print(f"SingletonMeta __new__, args:{args}, kwargs: {kwargs}")
        cls = super().__new__(mcs, *args, **kwargs)
        return cls

    def __init__(cls, *args, **kwargs):
        print(f"SingletonMetaMeta __init__, args:{args}, kwargs: {kwargs}")
        super(SingletonMetaMeta, cls).__init__(*args, **kwargs)
        cls.__instance = None

    def __call__(cls, *args, **kwargs):
        print(f"SingletonMeta __call__, args:{args}, kwargs: {kwargs}")
        if cls.__instance is None:
            cls.__instance = super(SingletonMetaMeta, cls).__call__(*args, **kwargs)
        return cls.__instance


class Singleton(metaclass=SingletonMetaMeta):
    def __new__(cls, *args, **kwargs):
        print(f"Singleton __new__, args:{args}, kwargs: {kwargs}")
        self = super().__new__(cls)
        return self

    def __init__(self, *args, **kwargs):
        print(f"Singleton __init__, args:{args}, kwargs: {kwargs}")
        super().__init__()

    def __call__(self, *args, **kwargs):
        print(f"Singleton __call__, args:{args}, kwargs: {kwargs}")


print("class created")

instance = Singleton(1)

print("instance created")

原始代码运行输出

SingletonMeta __new__, args:('Singleton', (), {'__module__': '__main__', '__qualname__': 'Singleton', '__new__': <function Singleton.__new__ at 0x7f790a09d5e0>, '__init__': <function Singleton.__init__ at 0x7f790a09d670>, '__call__': <function Singleton.__call__ at 0x7f790a09d700>, '__classcell__': <cell at 0x7f79238e61c0: empty>}), kwargs: {}
SingletonMetaMeta __init__, args:('Singleton', (), {'__module__': '__main__', '__qualname__': 'Singleton', '__new__': <function Singleton.__new__ at 0x7f790a09d5e0>, '__init__': <function Singleton.__init__ at 0x7f790a09d670>, '__call__': <function Singleton.__call__ at 0x7f790a09d700>, '__classcell__': <cell at 0x7f79238e61c0: SingletonMetaMeta object at 0x5631858c8fb0>}), kwargs: {}
class created
SingletonMeta __call__, args:(1,), kwargs: {}
Singleton __new__, args:(1,), kwargs: {}
Singleton __init__, args:(1,), kwargs: {}
instance created

错误尝试及原因

错误尝试1:直接调用cls.__new__(*args, **kwargs)

将元类__call__中的创建逻辑替换为:

instance = cls.__new__(*args, **kwargs)
instance.__init__(*args, **kwargs)

错误输出

SingletonMeta __new__, args:('Singleton', (), {'__module__': '__main__', '__qualname__': 'Singleton', '__new__': <function Singleton.__new__ at 0x7f5c0c9d35e0>, '__init__': <function Singleton.__init__ at 0x7f5c0c9d3670>, '__call__': <function Singleton.__call__ at 0x7f5c0c9d3700>, 'foo': <function Singleton.foo at 0x7f5c0c9d3790>, '__classcell__': <cell at 0x7f5c2621d1c0: empty>}), kwargs: {}
SingletonMetaMeta __init__, args:('Singleton', (), {'__module__': '__main__', '__qualname__': 'Singleton', '__new__': <function Singleton.__new__ at 0x7f5c0c9d35e0>, '__init__': <function Singleton.__init__ at 0x7f5c0c9d3670>, '__call__': <function Singleton.__call__ at 0x7f5c0c9d3700>, 'foo': <function Singleton.foo at 0x7f5c0c9d3790>, '__classcell__': <cell at 0x7f5c2621d1c0: SingletonMetaMeta object at 0x55ecc6137fb0>}), kwargs: {}
class created
SingletonMeta __call__, args:(1,), kwargs: {}
Singleton __new__, args:(), kwargs: {}
Traceback (most recent call last):
  File "/home/noam.s/src/uv_metadata/uv_metadata/utils/asd.py", line 41, in <module>
    instance = Singleton(1)
  File "/home/noam.s/src/uv_metadata/uv_metadata/utils/asd.py", line 16, in __call__
    instance = cls.__new__(*args, **kwargs)
  File "/home/noam.s/src/uv_metadata/uv_metadata/utils/asd.py", line 24, in __new__
    self = super().__new__(cls)
TypeError: super(type, obj): obj must be an instance or subtype of type

错误原因

__new__是类方法,必须显式传入类对象cls作为第一个参数,直接传*args会导致cls参数缺失,进而super()调用失败。


错误尝试2:调用super(SingletonMetaMeta, cls).__new__(*args, **kwargs)

替换为:

instance = super(SingletonMetaMeta, cls).__new__(*args, **kwargs)
instance.__init__(*args, **kwargs)

错误输出

SingletonMeta __new__, args:('Singleton', (), {'__module__': '__main__', '__qualname__': 'Singleton', '__new__': <function Singleton.__new__ at 0x7f7aefc875e0>, '__init__': <function Singleton.__init__ at 0x7f7aefc87670>, '__call__': <function Singleton.__call__ at 0x7f7aefc87700>, 'foo': <function Singleton.foo at 0x7f7aefc87790>, '__classcell__': <cell at 0x7f7b094d01c0: empty>}), kwargs: {}
SingletonMetaMeta __init__, args:('Singleton', (), {'__module__': '__main__', '__qualname__': 'Singleton', '__new__': <function Singleton.__new__ at 0x7f7aefc875e0>, '__init__': <function Singleton.__init__ at 0x7f7aefc87670>, '__call__': <function Singleton.__call__ at 0x7f7aefc87700>, 'foo': <function Singleton.foo at 0x7f7aefc87790>, '__classcell__': <cell at 0x7f7b094d01c0: SingletonMetaMeta object at 0x55b942d98fb0>}), kwargs: {}
class created
SingletonMeta __call__, args:(1,), kwargs: {}
Traceback (most recent call last):
  File "/home/noam.s/src/uv_metadata/uv_metadata/utils/asd.py", line 41, in <module>
    instance = Singleton(1)
  File "/home/noam.s/src/uv_metadata/uv_metadata/utils/asd.py", line 16, in __call__
    instance = super(SingletonMetaMeta, cls).__new__(*args, **kwargs)
TypeError: type.__new__(X): X is not a type object (int)

错误原因

super(SingletonMetaMeta, cls).__new__实际调用的是type.__new__,该方法的作用是创建类对象而非实例对象,传入实例化参数(如1)会导致类型不匹配。

正确拆分方式

修正后的元类__call__方法

def __call__(cls, *args, **kwargs):
    print(f"SingletonMeta __call__, args:{args}, kwargs: {kwargs}")
    if cls.__instance is None:
        # 显式传入cls作为__new__的第一个参数
        instance = cls.__new__(cls, *args, **kwargs)
        # 仅当返回的实例属于当前类时,才调用__init__
        if isinstance(instance, cls):
            instance.__init__(*args, **kwargs)
        cls.__instance = instance
    return cls.__instance

运行验证

修改后运行代码,输出与原始代码完全一致,说明手动拆分的逻辑与默认__call__行为等效。

关键说明

  1. __new__作为类方法,必须显式传入类对象cls作为第一个参数;
  2. 添加isinstance(instance, cls)判断是因为__new__可能返回父类实例或其他类型对象,此时无需调用当前类的__init__;
  3. 调用__init__时,实例对象会自动绑定到self参数,直接传入后续参数即可。

额外问题:修改class定义的执行逻辑

可以通过元类的__new__和__init__方法修改class Singleton(metaclass=SingletonMetaMeta):的执行逻辑。Python处理类定义的流程是:

  1. 收集类体中的属性与方法;
  2. 调用元类的__new__创建类对象;
  3. 调用元类的__init__初始化类对象。

例如,要在类创建时自动添加属性或执行校验,可以修改元类的__new__或__init__:

class SingletonMetaMeta(type):
    def __new__(mcs, name, bases, namespace, **kwargs):
        # 在类创建前向命名空间添加属性
        namespace['created_at'] = '2024-05-20'
        cls = super().__new__(mcs, name, bases, namespace, **kwargs)
        return cls

    def __init__(cls, name, bases, namespace, **kwargs):
        # 在类初始化时执行自定义逻辑
        print(f"类 {name} 创建完成,自动添加属性created_at: {cls.created_at}")
        super().__init__(name, bases, namespace, **kwargs)
        cls.__instance = None

定义Singleton类时,会自动触发上述逻辑,完成类创建过程的自定义控制。


内容的提问来源于Stack Exchange,提问作者Gulzar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 20:15:38