装饰类的装饰器导致类属性变化

(实在是外行新手,部分措辞可能没有那样专业,我尽量使描述准确。)

我了解到,若使用装饰器修饰一个函数,为了保证“一致性”,会使用functools.wraps修饰装饰器内部的函数(参考官方文档内的例子

现在我希望装饰一个类,我最初的目的是希望重写被修饰类父类的某个方法,于是我在转饰器内部定义了一个子类并返回它

from functools import wraps

class father():
    def something(self):
        pass

def decorator_my(cls):
    # @wraps(cls)
    class wrapped(cls):
        def something(self):
            print('do something')
    return wrapped

@decorator_my
class son(father):
    pass

print(son.__name__)

上面代码的运行结果是

wrapped

这是很好理解的,但我希望能得到son.__name__返回son,于是我尝试着加入wraps(取消上述注释),但是失败了
报错为

File "/usr/lib/python3.9/functools.py", line 58, in update_wrapper
    getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
AttributeError: 'mappingproxy' object has no attribute 'update'

我现在知道这样的错误是因为类的__dict__是一种只读的字典(mappingproxy),那是否有别的方法可以帮助我实现这个目的(例如son.__name__返回son

我的最初目的或许可以通过继承关系来实现,但我仍然希望知道这个有关装饰器问题的正确解决方法
谢谢

Jason990420
最佳答案

functools.wraps 适用于函数,但不适用于类,以下方式应该可行

from functools import wraps

class father():
    A = 1                           # update
    def something(self):
        pass

def decorator_my(cls):
    @wraps(cls, updated=())         # update
    class wrapped(cls):
        def something(self):
            print('do something')
    return wrapped                  # update

@decorator_my
class son(father):
    pass

print(son.__name__)                 # son

Python 3.3 中类的__dict__改为mappingproxy, 并且为只读属性; 所以在指定 updated=() 之后,类son 的属性__dict__updatefather 的属性__dict__

应该是没有问题,比如您仍然可以通过调用son.Ason().A从类father获得son类中没有的A的值。

>>> father.__dict__
mappingproxy({'A': 1,
              '__dict__': <attribute '__dict__' of 'father' objects>,
              '__doc__': None,
              '__module__': '__main__',
              '__weakref__': <attribute '__weakref__' of 'father' objects>,
              'something': <function father.something at 0x0000023F95366E50>})
>>> son.__dict__
mappingproxy({'__doc__': None,
              '__module__': '__main__',
              '__wrapped__': <class '__main__.son'>,
              'something': <function decorator_my.<locals>.wrapped.something at 0x0000023F95366F70>})
2年前 评论
讨论数量: 2
Jason990420

functools.wraps 适用于函数,但不适用于类,以下方式应该可行

from functools import wraps

class father():
    A = 1                           # update
    def something(self):
        pass

def decorator_my(cls):
    @wraps(cls, updated=())         # update
    class wrapped(cls):
        def something(self):
            print('do something')
    return wrapped                  # update

@decorator_my
class son(father):
    pass

print(son.__name__)                 # son

Python 3.3 中类的__dict__改为mappingproxy, 并且为只读属性; 所以在指定 updated=() 之后,类son 的属性__dict__updatefather 的属性__dict__

应该是没有问题,比如您仍然可以通过调用son.Ason().A从类father获得son类中没有的A的值。

>>> father.__dict__
mappingproxy({'A': 1,
              '__dict__': <attribute '__dict__' of 'father' objects>,
              '__doc__': None,
              '__module__': '__main__',
              '__weakref__': <attribute '__weakref__' of 'father' objects>,
              'something': <function father.something at 0x0000023F95366E50>})
>>> son.__dict__
mappingproxy({'__doc__': None,
              '__module__': '__main__',
              '__wrapped__': <class '__main__.son'>,
              'something': <function decorator_my.<locals>.wrapped.something at 0x0000023F95366F70>})
2年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!