Skip to content

Commit 21cdb71

Browse files
authored
bpo-40571: Make lru_cache(maxsize=None) more discoverable (GH-20019)
1 parent 4804b5b commit 21cdb71

File tree

4 files changed

+57
-1
lines changed

4 files changed

+57
-1
lines changed

Doc/library/functools.rst

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,32 @@ function for the purposes of this module.
2626

2727
The :mod:`functools` module defines the following functions:
2828

29+
.. decorator:: cache(user_function)
30+
31+
Simple lightweight unbounded function cache. Sometimes called
32+
`"memoize" <https://en.wikipedia.org/wiki/Memoization>`_.
33+
34+
Returns the same as ``lru_cache(maxsize=None)``, creating a thin
35+
wrapper around a dictionary lookup for the function arguments. Because it
36+
never needs to evict old values, this is smaller and faster than
37+
:func:`lru_cache()` with a size limit.
38+
39+
For example::
40+
41+
@cache
42+
def factorial(n):
43+
return n * factorial(n-1) if n else 1
44+
45+
>>> factorial(10) # no previously cached result, makes 11 recursive calls
46+
3628800
47+
>>> factorial(5) # just looks up cached value result
48+
120
49+
>>> factorial(12) # makes two new recursive calls, the other 10 are cached
50+
479001600
51+
52+
.. versionadded:: 3.9
53+
54+
2955
.. decorator:: cached_property(func)
3056

3157
Transform a method of a class into a property whose value is computed once

Lib/functools.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
# See C source code for _functools credits/copyright
1111

1212
__all__ = ['update_wrapper', 'wraps', 'WRAPPER_ASSIGNMENTS', 'WRAPPER_UPDATES',
13-
'total_ordering', 'cmp_to_key', 'lru_cache', 'reduce',
13+
'total_ordering', 'cache', 'cmp_to_key', 'lru_cache', 'reduce',
1414
'TopologicalSorter', 'CycleError',
1515
'partial', 'partialmethod', 'singledispatch', 'singledispatchmethod',
1616
'cached_property']
@@ -888,6 +888,15 @@ def cache_clear():
888888
pass
889889

890890

891+
################################################################################
892+
### cache -- simplified access to the infinity cache
893+
################################################################################
894+
895+
def cache(user_function, /):
896+
'Simple lightweight unbounded cache. Sometimes called "memoize".'
897+
return lru_cache(maxsize=None)(user_function)
898+
899+
891900
################################################################################
892901
### singledispatch() - single-dispatch generic function decorator
893902
################################################################################

Lib/test/test_functools.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1432,6 +1432,25 @@ def check_order_with_hash_seed(seed):
14321432
self.assertEqual(run1, run2)
14331433

14341434

1435+
class TestCache:
1436+
# This tests that the pass-through is working as designed.
1437+
# The underlying functionality is tested in TestLRU.
1438+
1439+
def test_cache(self):
1440+
@self.module.cache
1441+
def fib(n):
1442+
if n < 2:
1443+
return n
1444+
return fib(n-1) + fib(n-2)
1445+
self.assertEqual([fib(n) for n in range(16)],
1446+
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610])
1447+
self.assertEqual(fib.cache_info(),
1448+
self.module._CacheInfo(hits=28, misses=16, maxsize=None, currsize=16))
1449+
fib.cache_clear()
1450+
self.assertEqual(fib.cache_info(),
1451+
self.module._CacheInfo(hits=0, misses=0, maxsize=None, currsize=0))
1452+
1453+
14351454
class TestLRU:
14361455

14371456
def test_lru(self):
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Added functools.cache() as a simpler, more discoverable way to access the
2+
unbounded cache variant of lru_cache(maxsize=None).

0 commit comments

Comments
 (0)