实现具有与 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?)个选项:
- 我可以向我的 metaclass 添加一个空的
__init__
方法,但是由于我不确定 type.__init__
是否做了任何重要的事情,所以这可能不是一个好主意。
- 我可以实现调用
super().__init__(cls.__name__, cls.__bases__, vars(cls))
的 __init__
方法。
- 我可以使用 meta-metaclass 并覆盖它的
__call__
方法,而不是搞乱 __new__
和 __init__
.
- 奖励选项:也许我不应该尝试更改签名?
所以我的问题是:我列出的 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.fromkeys
、int.from_bytes
、bytes.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'
假设我想实现一个应该作为 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?)个选项:
- 我可以向我的 metaclass 添加一个空的
__init__
方法,但是由于我不确定type.__init__
是否做了任何重要的事情,所以这可能不是一个好主意。 - 我可以实现调用
super().__init__(cls.__name__, cls.__bases__, vars(cls))
的__init__
方法。 - 我可以使用 meta-metaclass 并覆盖它的
__call__
方法,而不是搞乱__new__
和__init__
. - 奖励选项:也许我不应该尝试更改签名?
所以我的问题是:我列出的 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.fromkeys
、int.from_bytes
、bytes.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'