使用与`type`不同的签名实现元类的正确方法是什么?

Ara*_*Fey 5 python metaclass

假设我想实现一个应该作为类工厂的元类.但与type构造函数不同,构造函数需要3个参数,我的元类应该可以调用而不带任何参数:

Cls1 = MyMeta()
Cls2 = MyMeta()
...
Run Code Online (Sandbox Code Playgroud)

为此,我定义了一个__new__没有参数的自定义方法:

class MyMeta(type):
    def __new__(cls):
        return super().__new__(cls, 'MyCls', (), {})
Run Code Online (Sandbox Code Playgroud)

但问题是python自动使用与__init__方法相同的参数__new__调用方法,因此尝试调用MyMeta()最终会抛出异常:

TypeError: type.__init__() takes 1 or 3 arguments
Run Code Online (Sandbox Code Playgroud)

这是有道理的,因为type可以用1或3个参数调用.但是解决这个问题的正确方法是什么?我看到3(4?)选项:

  • 我可以__init__在我的元类中添加一个空方法,但由于我不确定是否type.__init__有任何重要的事情,这可能不是一个好主意.
  • 我可以实现一个__init__调用的方法super().__init__(cls.__name__, cls.__bases__, vars(cls)).
  • 我可以使用元元类并覆盖它的__call__方法,而不是搞乱__new____init__.
  • 奖金选项:也许我不应该尝试更改签名?

所以我的问题是:我列出的3个解决方案是正确的还是隐藏在其中的任何微妙的错误?哪种解决方案最好(即最正确)?

wim*_*wim 4

偏离父签名的界面也是常规类中的可疑设计.你不需要额外的复杂的元类来进入这种混乱 - 你可以通过继承a datetime或其他东西导致相同的new/init混乱.

我希望有一个元类和一种简单的方法来创建该元类的实例.

Python中通常的模式是使用from_somethingclassmethod 编写工厂.要采取从不同的init签名产生的datetime实例的例子,例如存在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, (), {})
Run Code Online (Sandbox Code Playgroud)

用法:

>>> 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'
Run Code Online (Sandbox Code Playgroud)