如何在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__行为等效。
关键说明
__new__作为类方法,必须显式传入类对象cls作为第一个参数;- 添加
isinstance(instance, cls)判断是因为__new__可能返回父类实例或其他类型对象,此时无需调用当前类的__init__; - 调用
__init__时,实例对象会自动绑定到self参数,直接传入后续参数即可。
额外问题:修改class定义的执行逻辑
可以通过元类的__new__和__init__方法修改class Singleton(metaclass=SingletonMetaMeta):的执行逻辑。Python处理类定义的流程是:
- 收集类体中的属性与方法;
- 调用元类的
__new__创建类对象; - 调用元类的
__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
相关产品推荐
相关产品推荐

