用与类型不同的签名实现元类的正确方法是什么? [英] What's the correct way to implement a metaclass with a different signature than `type`?

查看:43
本文介绍了用与类型不同的签名实现元类的正确方法是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

说我想实现一个应作为类工厂的元类.但是与 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 if type.__init__ does anything important, this might not be a good idea.
  • I could implement an __init__ method that calls super().__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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆