我遇到了 functools.update_wrapper 的一个特殊行为:它用被包装对象的 __dict__ 覆盖了包装对象的 functools.update_wrapper - 这可能会妨碍它在嵌套装饰器时的使用。

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

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

但是,我花了很长时间才完成这项工作 - 起初,我没有在 functools.update_wrapper 调用中包含 'updated=()' 参数。但是当它被遗漏时,嵌套装饰器不起作用 - 在这种情况下,输出是
Retrieving from memory
expensive_function working on Expensive Calculation

IE。外层装饰器直接调用最内层的包装函数。原因(我花了一段时间才理解)是 __dict__ 将包装器的 __dict__ 属性更新为包装参数的 updated=() 属性——这会使内部装饰器短路,除非添加 ojit_code 参数。

我的问题:这种行为是有意为之 - 为什么? (Python 3.7.1)

最佳答案

使包装函数看起来像它包装的函数是 update_wrapper 的重点,其中包括 __dict__ 条目。它不会替换 __dict__ ;它调用 update

如果 update_wrapper 没有这样做,那么如果一个装饰器在一个函数上设置属性,而另一个装饰器包装了修改后的函数:

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

包装函数不会设置属性,从而使其与查找这些属性的代码不兼容。

关于python - 为什么 functools.update_wrapper 更新包装器对象中的 __dict__ ?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/53602825/

10-14 01:07