为什么functools.update_wrapper更新包装对象中的__dict __?

时间:2018-12-03 22:19:46

标签: python python-decorators functools

我遇到了functools.update_wrapper的一种特殊行为:它用包装对象的__dict__覆盖了包装对象的import functools class cached: cache_type = 'memory' def __init__(self, fcn): super().__init__() self.fcn = fcn functools.update_wrapper(self, fcn, updated=()) def __call__(self, *args): print("Retrieving from", type(self).cache_type) return self.fcn(*args) class diskcached(cached): cache_type = 'disk' @cached @diskcached def expensive_function(what): print("expensive_function working on", what) expensive_function("Expensive Calculation") ,这可能会妨碍在嵌套装饰器时使用它。

作为一个简单的示例,假设我们正在编写一个将数据缓存在内存中的装饰器类,以及另一个将数据缓存至文件的装饰器类。下面的示例演示了这一点(我简化了示例,省略了所有缓存逻辑,但是希望它演示了问题):

Retrieving from memory
Retrieving from disk
expensive_function working on Expensive Calculation

此示例按预期方式工作-其输出为

Retrieving from memory
expensive_function working on Expensive Calculation

但是,完成这项工作花了我很长时间-首先,我不希望在functools.update_wrapper调用中包含'updated =()'参数。但是,如果不这样做,则嵌套装饰器将不起作用-在这种情况下,输出为

functools.update_wrapper

即外部装饰器直接调用最内部的包装函数。原因(花了我一段时间)是因为__dict__将包装器的__dict__属性更新为包装后的参数的updated=()属性-短路了内部装饰器,除非添加x = int(x) 参数。

我的问题:这种行为是故意的吗?为什么? (Python 3.7.1)

1 个答案:

答案 0 :(得分:3)

使包装函数看起来像包装的函数是update_wrapper的重点,其中包括__dict__项。它不会取代__dict__;它会调用update

如果update_wrapper不这样做,那么如果一个装饰器在一个函数上设置属性,而另一个装饰器则将修改后的函数包装起来:

@decorator_with_update_wrapper
@decorator_that_sets_attributes
def f(...):
    ...

wrapper函数没有设置属性,使其与查找那些属性的代码不兼容。