This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: functools.lru_cache seems to not work when renaming decorated functions
Type: behavior Stage:
Components: Library (Lib) Versions: Python 3.5
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: ncoghlan, rhettinger, steven.daprano, Федор Лянгузов
Priority: normal Keywords:

Created on 2016-09-01 16:08 by Федор Лянгузов, last changed 2022-04-11 14:58 by admin. This issue is now closed.

Files
File name Uploaded Description Edit
lru_cache_test.py Федор Лянгузов, 2016-09-01 16:08 3 tests of functools.lru_cache with one failing
Messages (4)
msg274149 - (view) Author: Федор Лянгузов (Федор Лянгузов) * Date: 2016-09-01 16:08
Greetings,

I've encountered strange behavior when using functools.lru_cache as a function (not as a decorator): it is at least miscounting misses, but probably not work at all, when the result of functools.lru_cache()(func) is saved in variable other than 'func'. Consider this snippet:

def factorial(n):
    if n == 0:
        return 1
    else:
        return n * factorial(n-1)

f = functools.lru_cache()(factorial)
f(20)
print(f.cache_info())

Output should be: CacheInfo(hits=0, misses=21, maxsize=128, currsize=21)
Instead it is: CacheInfo(hits=0, misses=1, maxsize=128, currsize=1)

I'm using Python 3.5.2 64bit on Windows 7 Professional 64bit.
I've written 3 unittests (using built-in module), which are attached as a file. I don't know how to comment them (conceptually, not syntactically), sorry.

Fedor
msg274151 - (view) Author: Steven D'Aprano (steven.daprano) * (Python committer) Date: 2016-09-01 16:35
This behaviour is expected. The factorial function calls itself, it doesn't call "f", but it is "f" which has the cache. So the call to f() goes through the cache, misses, and then calls factorial(), which has no cache.

In effect, what you have written is something like:

def factorial(n):
    if n == 0:
        return 1
    else:
        return n * factorial(n-1)

def f(n):
    if n in f.cache:
        return f.cache[n]
    else:
        x = f.cache[n] = factorial(n)
        return x

f.cache = lru_cache()
msg274152 - (view) Author: Steven D'Aprano (steven.daprano) * (Python committer) Date: 2016-09-01 16:39
In case it isn't obvious, my example is meant to be pseudo-code, not the exact implementation used.
msg274167 - (view) Author: Федор Лянгузов (Федор Лянгузов) * Date: 2016-09-01 18:44
Ok, thank you very much, i've got a little smarter today. Now i understand, that user_function (in this case factorial) is not modified by decorator, it is my job to change factorial to wrapper by assignment. Enlightning.
History
Date User Action Args
2022-04-11 14:58:35adminsetgithub: 72120
2016-09-01 18:44:15Федор Лянгузовsetstatus: open -> closed
resolution: not a bug
messages: + msg274167
2016-09-01 16:39:53steven.dapranosetmessages: + msg274152
2016-09-01 16:35:53steven.dapranosetnosy: + steven.daprano
messages: + msg274151
2016-09-01 16:25:32abarrysetnosy: + rhettinger, ncoghlan
2016-09-01 16:08:44Федор Лянгузовcreate