类型错误:“dict”对象的描述符“值”不适用于“类型”对象

问题描述

我制作了一个装饰器,它可以修改一个类,以便除 initclass 之外的任何方法在运行之前打印“Hello World”。问题是我从 dict 继承时不断收到意外错误消息。这是什么意思,我该如何解决

代码

from functools import wraps


# Print hello world when any method is called
def hello_world(cls):
    for attr in dir(cls):
        # Only modify methods that aren't init or class
        if not callable(getattr(cls,attr)) or any([method in attr for method in ["init","class"]]):
            continue
        old_method = getattr(cls,attr)

        # Modify method to print "Hello World" before running
        @wraps(getattr(cls,attr))
        def new_method(self,*args,**kwargs):
            print("Hello World!")
            return old_method(self,**kwargs)

        # Update class with modified method
        setattr(cls,attr,new_method)
    return cls


@hello_world
class Custom(dict):
    pass


dictionary = Custom()
dictionary["Key"] = "Value"
print(dictionary)

输出

世界你好!
回溯(最近一次调用最后一次):
文件“”,第 22 行,在
字典 = 自定义()
文件“”,第 12 行,在 new_method
返回 old_method(self,**kwargs)
类型错误:“dict”对象的描述符“值”不适用于“类型”对象

解决方法

多亏了 Tim Roberts 的洞察力,我才能够拼凑出解决问题的方法。我不得不向排除列表添加更多属性以避免无限递归,并且我添加了一个额外的调试语句,以便您可以在打印“Hello world!”时看到正在调用哪个方法。

无论如何,以下工作:

from functools import wraps


# Print hello world when any method is called
def hello_world(cls):
    for attr in dir(cls):
        # Only modify methods that aren't init or class
        if not callable(getattr(cls,attr)):
            continue

        if attr in ("__class__","__subclasshook__","__init__","__init_subclass__","__str__","__repr__"):
            continue

        old_method = getattr(cls,attr)

        def do_override(cls,attr,old_method):
            # Modify method to print "Hello World" before running
            @wraps(getattr(cls,attr))
            def new_method(self,*args,**kwargs):
                print("Hello World!")
                print("attr:",attr)
                return old_method(self,**kwargs)

            # Update class with modified method
            setattr(cls,new_method)

        do_override(cls,old_method)

    return cls


@hello_world
class Custom(dict):
    pass


dictionary = Custom()
dictionary["Key"] = "Value"
print(dictionary)

运行时,它产生:

Hello World!
attr: __new__
Hello World!
attr: __setitem__
{'Key': 'Value'}

do_override 函数捕获 clsattrold_method 的值,以便后续循环迭代不会影响之前的 绑定值。

在输出中,第一个方法调用来自创建类实例时,第二个来自设置值时。

这可能需要做一些工作才能满足您的需求,您可能需要向排除列表添加一些额外的属性,但这应该能让您解决遇到的问题。