通过覆盖 __new__ 来装饰 class 方法不起作用?

Decorating class methods by overriding __new__ doesn't work?

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

装饰者:

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

现在我想装饰我的class的所有方法。一种简单的方法是在我的 class 之上使用 @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

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__ 的签名在 metaclass 中不同?

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

您似乎混淆了 __new__ 和 metaclasses。调用 __new__ 来创建一个新对象(来自 class 的实例,来自 metaclass 的 class),它不是 'class created' 钩子。

正常模式是:

  • Foo(...) 被翻译成 type(Foo).__call__(Foo, ...),请参阅 special method lookups 了解原因。 class 的 type() 是元 class.
  • Foo是自定义的Python时使用的标准type.__call__实现class将调用__new__创建一个新实例, 如果结果确实是 Foo class:

    的实例,则对该实例调用 __init__ 方法
    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 class 本身被创建时 Foo.__new__ 不会被调用,只有当 Foo 实例 是已创建。

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

相同的 __call__ -> __new__ 并且可能 __init__ 过程适用于 metaclasses。 class Foo: ... 语句是通过调用 metaclass 创建一个 class 对象来实现的,传入 3 个参数:class 名称,class 基数,以及class 正文,通常作为字典。使用 class Spam(metaclass = debugmeta): ...,这意味着 debugmeta('Spam', (), {...}) 被调用,这意味着 debugmeta.__new__(debugmeta, 'Spam', (), {...}) 被调用。

您的第一次尝试 a,设置 Spam.__new__ 无效,因为您 没有创建 class 对象 那里。相反,super().__new__(cls) 创建一个没有属性的空 Spam() 实例 ,因此 vars() returns 一个空字典和 debugmethods()最终什么都不做。

如果你想挂钩到 class 创建,那么你需要一个 metaclass.