用与类型不同的签名实现元类的正确方法是什么? [英] What's the correct way to implement a metaclass with a different signature than `type`?
问题描述
说我想实现一个应作为类工厂的元类.但是与 type
构造函数不同,该构造函数需要3个参数,我的元类应该可以无任何参数地调用:
Say I want to implement a metaclass that should serve as a class factory. But unlike the type
constructor, which takes 3 arguments, my metaclass should be callable without any arguments:
Cls1 = MyMeta()
Cls2 = MyMeta()
...
为此,我定义了一个自定义 __new__
没有参数的方法:
For this purpose I defined a custom __new__
method with no parameters:
class MyMeta(type):
def __new__(cls):
return super().__new__(cls, 'MyCls', (), {})
但是问题是python自动调用 __init__
方法与__new__
方法具有相同的参数,因此尝试调用MyMeta()
最终会引发异常:
But the problem is that python automatically calls the __init__
method with the same arguments as the __new__
method, so trying to call MyMeta()
ends up throwing an exception:
TypeError: type.__init__() takes 1 or 3 arguments
这很有意义,因为type
可以使用1或3个参数来调用.但是解决此问题的正确方法是什么?我看到3(4?)个选项:
Which makes sense, since type
can be called with 1 or 3 arguments. But what's the correct way to fix this? I see 3 (4?) options:
- 我可以在我的元类中添加一个空的
__init__
方法,但是由于我不确定type.__init__
是否有重要作用,所以这可能不是一个好主意. - 我可以实现调用
super().__init__(cls.__name__, cls.__bases__, vars(cls))
的__init__
方法. - 我可以使用元元类并覆盖其
__call__
方法,而不用弄乱__new__
和__init__
. - 奖励选项:也许我不应该尝试更改签名?
- I could add an empty
__init__
method to my metaclass, but since I'm not sure iftype.__init__
does anything important, this might not be a good idea. - I could implement an
__init__
method that callssuper().__init__(cls.__name__, cls.__bases__, vars(cls))
. - I could use a meta-metaclass and override its
__call__
method, rather than messing with__new__
and__init__
. - Bonus option: Maybe I shouldn't try to change the signature?
所以我的问题是:我列出的3个解决方案是否正确,或者其中隐藏着任何细微的错误?哪种解决方案是最好的(即最正确的)?
So my question is: Are the 3 solutions I listed correct or are there any subtle bugs hidden in them? Which solution is best (i.e. the most correct)?
推荐答案
与父签名不同的接口是.您不需要元类的额外复杂性即可陷入这种混乱-您可以通过子类化datetime
或类似内容来引起相同的新的/初始的混乱.
An interface deviating from the parent signature is a questionable design in regular classes too. You don't need the extra complexity of metaclasses to get into this kind of mess - you can cause the same new/init jumble by subclassing a datetime
or whatever.
我想要一个元类和一种简单的方法来创建该元类的实例.
I want to have a metaclass and an easy way to create instances of that metaclass.
Python中通常的模式是使用from_something
类方法编写工厂.以从不同的初始化签名创建日期时间实例为例,例如,
The usual pattern in Python is to write a factory using a from_something
classmethod. To take the example of creating datetime instances from a different init signature, there is for example datetime.fromtimestamp
, but you have many other examples too (dict.fromkeys
, int.from_bytes
, bytes.fromhex
...)
这里没有特定于元类的内容,因此请使用相同的模式:
There is nothing specific to metaclasses here, so use the same pattern:
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'
这篇关于用与类型不同的签名实现元类的正确方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!