说我想实现一个应作为类工厂的元类。但是与type
构造函数需要3个参数的情况不同,我的元类应该可以在没有任何参数的情况下被调用:
Cls1 = MyMeta()
Cls2 = MyMeta()
...
为此,我定义了一个不带参数的自定义__new__
方法:
class MyMeta(type):
def __new__(cls):
return super().__new__(cls, 'MyCls', (), {})
但是问题是python自动使用与__new__
方法相同的参数调用__init__
方法,因此尝试调用MyMeta()
最终会引发异常:
TypeError: type.__init__() takes 1 or 3 arguments
这很有意义,因为type
可以使用1或3个参数来调用。但是解决此问题的正确方法是什么?我看到3(4?)个选项:
__init__
方法,但是由于我不确定type.__init__
是否做任何重要的事情,所以这可能不是一个好主意。__init__
的{{1}}方法。super().__init__(cls.__name__, cls.__bases__, vars(cls))
方法,而不用弄乱__call__
和__new__
。所以我的问题是:我列出的3个解决方案是否正确,或者其中隐藏着任何细微的错误?哪种解决方案是最好的(即最正确的)?
答案 0 :(得分:4)
与父签名不同的接口也是常规类中的questionable design。您不需要复杂的元类类来陷入这种混乱-您可以通过子类化datetime
或类似的东西来引起相同的新的/初始的混乱。
我想要一个元类和一种简单的方法来创建该元类的实例。
Python中通常的模式是使用from_something
类方法编写工厂。以从不同的初始化签名创建日期时间实例为例,例如有datetime.fromtimestamp
,但是您还有许多其他示例(dict.fromkeys
,int.from_bytes
,bytes.fromhex
。 ..)
这里没有特定于元类的内容,因此请使用相同的模式:
class MyMeta(type):
@classmethod
def from_no_args(cls, name=None):
if name is None:
name = cls.__name__ + 'Instance'
return cls(name, (), {})
用法:
>>> class A(metaclass=MyMeta):
... pass
...
>>> B = MyMeta.from_no_args()
>>> C = MyMeta.from_no_args(name='C')
>>> A.__name__
'A'
>>> B.__name__
'MyMetaInstance'
>>> C.__name__
'C'