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

时间:2018-09-05 13:10:04

标签: python metaclass

说我想实现一个应作为类工厂的元类。但是与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个解决方案是否正确,或者其中隐藏着任何细微的错误?哪种解决方案是最好的(即最正确的)?

1 个答案:

答案 0 :(得分:4)

与父签名不同的接口也是常规类中的questionable design。您不需要复杂的元类类来陷入这种混乱-您可以通过子类化datetime或类似的东西来引起相同的新的/初始的混乱。

  

我想要一个元类和一种简单的方法来创建该元类的实例。

Python中通常的模式是使用from_something类方法编写工厂。以从不同的初始化签名创建日期时间实例为例,例如有datetime.fromtimestamp,但是您还有许多其他示例(dict.fromkeysint.from_bytesbytes.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'