用重写的方法来装饰类方法不起作用?

2024-10-03 11:12:15 发布

您现在位置:Python中文网/ 问答频道 /正文

我想装饰我班上所有的方法。我在这里写了一个小装饰的例子,以供说明。在

装饰工:

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__

^{pr2}$

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. 为什么方法{}的签名在元类中不同?在

有人能帮我理解我在这里遗漏了什么吗。在


Tags: 方法debugnewreturndefargsmsg装饰
1条回答
网友
1楼 · 发布于 2024-10-03 11:12:15

您似乎混淆了__new__和元类。^调用{}来创建一个新对象(类中的实例,元类中的类),它不是“类创建”钩子。在

正常模式是:

  • Foo(...)被转换为type(Foo).__call__(Foo, ...),请参见{a1}了解原因。类的type()是它的元类。在
  • Foo是自定义Python类时使用的标准type.__call__实现将调用__new__来创建一个新实例,如果结果确实是Foo类的实例,则在该实例上调用__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实例时,Foo.__new__不会被调用。

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

相同的__call__->;__new__和{}过程也适用于元类。一个class Foo: ...语句是通过调用元类来创建类对象来实现的,传递3个参数:类名、类基和类主体,通常作为字典。对于class Spam(metaclass = debugmeta): ...,这意味着调用debugmeta('Spam', (), {...}),这意味着调用{}。在

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

如果你想钩住类的创建,那么你需要一个元类。在

相关问题 更多 >