实现具有与 type 不同签名的元类的正确方法是什么?

What's the correct way to implement a metaclass with a different signature than `type`?

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

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

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

所以我的问题是:我列出的 3 个解决方案是否正确,或者它们是否隐藏了任何细微的错误?哪种解决方案最好(即最正确)?

偏离父签名的接口在常规 类 中也是 questionable design。你不需要 meta类 的额外复杂性来陷入这种混乱 - 你可以通过子类化 datetime 或其他任何东西来导致相同的 new/init 混乱。

I want to have a metaclass and an easy way to create instances of that metaclass.

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

这里没有什么特定于 meta类 的,所以使用相同的模式:

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'