代码之家  ›  专栏  ›  技术社区  ›  Aran-Fey Kevin

用不同于“type”的签名实现元类的正确方法是什么?

  •  5
  • Aran-Fey Kevin  · 技术社区  · 6 年前

    假设我想实现一个元类,它应该作为一个类工厂。但不同于 type 构造函数,它有3个参数,我的元类应该可以在没有任何参数的情况下调用:

    Cls1 = MyMeta()
    Cls2 = MyMeta()
    ...
    

    为此,我定义了一个习惯 __new__

    class MyMeta(type):
        def __new__(cls):
            return super().__new__(cls, 'MyCls', (), {})
    

    但问题是python会自动调用 __init__ 方法的参数与 __新建__ 方法,所以尝试调用 MyMeta()

    TypeError: type.__init__() takes 1 or 3 arguments
    

    这是有道理的,因为 类型 可以用1或3个参数调用。但是怎样才能解决这个问题呢?我看到3(4?)选项:

    • __初始化__ 方法,但由于我不确定 type.__init__ 做任何重要的事,这可能不是个好主意。
    • __初始化__ 调用 super().__init__(cls.__name__, cls.__bases__, vars(cls))
    • __call__ 方法,而不是 __新建__
    • 额外的选择:也许我不应该尝试改变签名?

    1 回复  |  直到 6 年前
        1
  •  4
  •   wim    6 年前

    与父签名不同的接口是 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'