Python 什么';使用与“type”不同的签名实现元类的正确方法是什么?
假设我想要实现一个元类,它应该作为类工厂。但与构造函数不同的是,构造函数需要3个参数,我的元类应该可以在没有任何参数的情况下调用: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
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
。