【问题标题】:Decorating class methods by overriding __new__ doesn't work?通过覆盖 __new__ 来装饰类方法不起作用?
【发布时间】:2019-06-28 11:26:57
【问题描述】:

我想装饰我班级的所有方法。我在这里写了一个示例小装饰器用于说明目的。

装饰者:

def debug(func):
    msg = func.__name__

    @wraps(func)
    def wrapper(*args, **kwargs):
        print(msg)
        return func(*args, **kwargs)
    return wrapper 

def debugmethods(cls):
    for key, val in vars(cls).items():
        if callable(val):
            setattr(cls, key, debug(val))
    return cls

现在我想装饰我班级的所有方法。一种简单的方法是在我的课堂上使用@debugmethods 注释,但我试图了解其他两种不同的方法。

a) 覆盖__new__

class Spam:
    def __new__(cls, *args, **kwargs):
        clsobj = super().__new__(cls)
        clsobj = debugmethods(clsobj)
        return clsobj

    def __init__(self):
        pass

    def foo(self):
        pass

    def bar(self):
        pass

spam = Spam()
spam.foo()

b) 编写元类

class debugmeta(type):
    def __new__(cls, clsname, bases, clsdict):
        clsobj = super().__new__(cls, clsname, bases, clsdict)
        clsobj = debugmethods(clsobj)
        return clsobj

class Spam(metaclass = debugmeta):     
    def foo(self):
        pass

    def bar(self):
        pass

spam = Spam()
spam.foo()

我不确定

  1. 为什么“a) 覆盖 __new__”不起作用?
  2. 为什么方法__new__ 的签名在元类中不同?

有人可以帮助我了解我在这里缺少什么。

【问题讨论】:

  • 元类创建类,类创建实例。 __new__ 方法签名反映了这一点。
  • 有道理。您的意思是说元类中的__new__ 方法也被a) 调用?如果是,那么__new__ 什么时候被调用?谁在调用?
  • 否,a) 中的 __new__ 被调用以创建Spam 类的实例super().__new__() 返回该实例。你那里没有课程。

标签: python metaclass python-internals


【解决方案1】:

您似乎对__new__ 和元类感到困惑。 __new__ 被调用来创建一个新对象(一个类的实例,一个元类的类),它不是一个“类创建”钩子。

正常模式是:

  • Foo(...) 被翻译成 type(Foo).__call__(Foo, ...),请参阅 special method lookups 了解为什么会这样。一个类的type() 是它的元类。
  • Foo 是自定义Python 类时使用的标准type.__call__ 实现将调用__new__ 来创建一个新实例,然后如果结果确实是@ 的实例,则对该实例调用__init__ 方法987654332@班:

    def __call__(cls, *args, **kwargs):  # Foo(...) -> cls=Foo
        instance = cls.__new__(cls, *args, **kwargs)  # so Foo.__new__(Foo, ...)
        if isinstance(instance, cls):
            instance.__init__(*args, **kwargs)        # Foo.__init__(instance, ...)
        return instance
    

    所以Foo.__new__Foo类本身被创建时不会被调用,只有在Fooinstances被创建时。

您通常不需要在类中使用__new__,因为__init__ 足以初始化实例的属性。但是对于不可变类型,比如inttuple,你只能使用__new__来准备新的实例状态,因为你不能改变一个不可变对象的属性。创建的。当您想要更改 ClassObj() 生成的实例类型(例如创建单例或生成专门的子类)时,__new__ 也很有帮助。

同样的__call__ -> __new____init__ 过程也适用于元类。 class Foo: ... 语句通过调用元类来创建类对象来实现,传入 3 个参数:类名、类基和类主体,通常作为字典。使用class Spam(metaclass = debugmeta): ...,表示调用debugmeta('Spam', (), {...}),表示调用debugmeta.__new__(debugmeta, 'Spam', (), {...})

您的第一次尝试a,设置Spam.__new__ 不起作用,因为您没有在那里创建类对象。相反,super().__new__(cls) 创建了一个没有属性的空 Spam() instance,因此 vars() 返回一个空字典,而 debugmethods() 最终什么都不做。

如果你想加入类创建,那么你需要一个元类。

【讨论】:

  • 这是一个了不起的解释。非常感谢您的详细撰写,这消除了我的疑问。
  • 非常有用,谢谢 Martijn!我正在寻找标准的type.__call__ 实现。你是从哪里弄来的?
  • @Maggyero:来自C source code
  • 感谢您的参考。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-31
  • 2021-10-04
  • 2014-01-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多