Python:设置属性是一个 lambda,只是 returns 一个字典

Python: Setting attribute that is a lambda that just returns a dictionary

在了解装饰器和装饰器工厂的过程中,我检查了 @functools.lru_cache 的源代码,因为它允许使用一个实现来实现这两种用法。我发现了一些让我感兴趣的东西。在下面的语句中 wrapper.cache_parameters = lambda : {'maxsize': maxsize, 'typed': typed} 从这个 piece of code from the CPython implementation 中提取:

def lru_cache(maxsize=128, typed=False):
    """<docstring ommited for brevity>"""
    if isinstance(maxsize, int):
        # Negative maxsize is treated as 0
        if maxsize < 0:
            maxsize = 0
    elif callable(maxsize) and isinstance(typed, bool):
        # The user_function was passed in directly via the maxsize argument
        user_function, maxsize = maxsize, 128
        wrapper = _lru_cache_wrapper(user_function, maxsize, typed, _CacheInfo)
        wrapper.cache_parameters = lambda : {'maxsize': maxsize, 'typed': typed}
        return update_wrapper(wrapper, user_function)
    elif maxsize is not None:
        raise TypeError(
            'Expected first argument to be an integer, a callable, or None')
    
    def decorating_function(user_function):
        wrapper = _lru_cache_wrapper(user_function, maxsize, typed, _CacheInfo)
        wrapper.cache_parameters = lambda : {'maxsize': maxsize, 'typed': typed}
        return update_wrapper(wrapper, user_function)
    
    return decorating_function

创建仅 returns 字典而不是直接将字典设置为属性的 lambda 的目的是什么?

After thinking a bit on the subject I came to this conclusion. But if there's another reason or you have a more complete explanation, please post an answer so I can accept it :)

字典是 Python 中的可变对象。这意味着任何拥有字典引用的人都可以更改它。所以我认为这里使用 lambda 是使它成为 read-only 属性的一个技巧。看看下面的例子:

def f():
    pass

f.a = lambda: {"a": 1}

ref = f.a()
ref

输出:

{'a': 1}

如果您更改返回的字典,则不会影响以下对 lambda 的调用,因为每次调用都会创建一个新字典:

ref["b"] = 2
f.a()

输出:

{'a': 1}

正如您在示例中看到的,返回的字典显然仍然是可变的,但更改它对 lambda 本身的返回值没有影响。

在添加这一行的commit中,我还发现了以下支持我理论的注释:

The wrapped function is instrumented with a :func:cache_parameters function that returns a new :class:dict showing the values for maxsize and typed. This is for information purposes only. Mutating the values has no effect.