Reputation: 2522
While learning about decorator and decorator factories, I checked the source code of the @functools.lru_cache
since it allows both usages using one single implementation. And I spotted something that is intriguing me. In the following statement wrapper.cache_parameters = lambda : {'maxsize': maxsize, 'typed': typed}
extracted from this 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
What's the purpose of creating a lambda that only returns a dictionary instead of setting the dictionary directly to the attribute?
Upvotes: 0
Views: 121
Reputation: 2522
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 :)
Dictionaries are mutable objects in Python. This means that anyone that has a reference to a dictionary can change it. So I think that the usage of the lambda here is a trick to make this a read-only attribute. Take a look at the following example:
def f():
pass
f.a = lambda: {"a": 1}
ref = f.a()
ref
Outputs:
{'a': 1}
And if you change the returned dictionary, it will have no impact on the following calls to the lambda because a new dictionary is created in every call:
ref["b"] = 2
f.a()
Outputs:
{'a': 1}
As you can see in the example, the returned dictionary is obviously still mutable, but changing it has no effect in the returned value by the lambda itself.
In the commit that added this line, I also found the following note that supports my theory:
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.
Upvotes: 3