Python 什么';使用与“type”不同的签名实现元类的正确方法是什么?

Python 什么';使用与“type”不同的签名实现元类的正确方法是什么?,python,metaclass,Python,Metaclass,假设我想要实现一个元类,它应该作为类工厂。但与构造函数不同的是,构造函数需要3个参数,我的元类应该可以在没有任何参数的情况下调用: Cls1 = MyMeta() Cls2 = MyMeta() ... 为此,我定义了一个没有参数的自定义方法: class MyMeta(type): def __new__(cls): return super().__new__(cls, 'MyCls', (), {}) 但问题是python会自动调用与\uuuuu new\uuu

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

Cls1 = MyMeta()
Cls2 = MyMeta()
...
为此,我定义了一个没有参数的自定义方法:

class MyMeta(type):
    def __new__(cls):
        return super().__new__(cls, 'MyCls', (), {})
但问题是python会自动调用与
\uuuuu new\uuuuu
方法参数相同的方法,因此尝试调用
MyMeta()
会引发异常:

TypeError: type.__init__() takes 1 or 3 arguments
这很有意义,因为
type
可以用1或3个参数调用。但解决这个问题的正确方法是什么?我看到3(4?)个选项:

  • 我可以在元类中添加一个空的
    \uuuuuu init\uuuuu
    方法,但是由于我不确定
    type.\uuuuuuuu init\uuuuu
    是否做了任何重要的事情,这可能不是一个好主意
  • 我可以实现一个调用
    super()的
    \uuuuuuuu init\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
  • 我可以使用一个元类并覆盖它的
    \uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
    方法,而不是把
    \uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
  • 奖金选项:也许我不应该尝试更改签名

所以我的问题是:我列出的3种解决方案是正确的,还是其中隐藏着任何细微的缺陷?哪个解决方案是最好的(即最正确的)?

在常规类中,偏离父签名的接口也是一个问题。您不需要元类的额外复杂性来陷入这种混乱-您可以通过子类化
datetime
或任何东西来造成相同的新/init混乱

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

Python中通常的模式是使用
from\u something
classmethod编写工厂。以从不同的init签名创建日期时间实例为例,例如有,但您也有许多其他示例(
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'

1.是否要求您的元类是
类型的子类
?2.您能否添加一点关于更改签名的用例的背景知识(许多元类用例最近可能使用其他方法,例如
\uuuu init\u subclass\uuuu
)@wim说实话,这是一个相当理论化的问题;我在回答另一个问题时偶然发现了这个问题。所以我真的不知道元类将如何使用。但我确实认为,在某些情况下,类成为
类型的子类
会很有用。所以你可以想象这样的场景:我想要一个元类和一个简单的方法来创建该元类的实例(即类工厂)。如果更改类的签名不是正确的方法,您可以建议另一种解决方案。我认为这实际上比“有问题”更糟糕:更改签名会阻止您在
class
语句中使用元类,因为
class Foo(metaclass=MyMeta):
只会引发异常。因此,另一种构造函数肯定是更好的解决方案。当然,这是可能的,但是您需要正确地实现协作继承。几乎同样的问题也发生了对不起,什么合作继承?这里没有多重继承,是吗?合作继承独立于多重继承。这只意味着所有相关的类都必须合作,以确保正确使用
super
。^这是。如果您要更改签名,您必须接受
*args
**kwargs
。如果您接受
*args
**kwargs
,每个人都必须接受
*args
**kwargs