[Python-checkins] bpo-40571: Make lru_cache(maxsize=None) more discoverable (GH-20019)

Raymond Hettinger webhook-mailer at python.org
Mon May 11 20:00:58 EDT 2020


https://github.com/python/cpython/commit/21cdb711e3b1975398c54141e519ead02670610e
commit: 21cdb711e3b1975398c54141e519ead02670610e
branch: master
author: Raymond Hettinger <rhettinger at users.noreply.github.com>
committer: GitHub <noreply at github.com>
date: 2020-05-11T17:00:53-07:00
summary:

bpo-40571: Make lru_cache(maxsize=None) more discoverable (GH-20019)

files:
A Misc/NEWS.d/next/Library/2020-05-09-15-38-25.bpo-40571.kOXZGC.rst
M Doc/library/functools.rst
M Lib/functools.py
M Lib/test/test_functools.py

diff --git a/Doc/library/functools.rst b/Doc/library/functools.rst
index 856c1c790ae36..204e66ae5ac40 100644
--- a/Doc/library/functools.rst
+++ b/Doc/library/functools.rst
@@ -26,6 +26,32 @@ function for the purposes of this module.
 
 The :mod:`functools` module defines the following functions:
 
+.. decorator:: cache(user_function)
+
+   Simple lightweight unbounded function cache.  Sometimes called
+   `"memoize" <https://en.wikipedia.org/wiki/Memoization>`_.
+
+   Returns the same as ``lru_cache(maxsize=None)``, creating a thin
+   wrapper around a dictionary lookup for the function arguments.  Because it
+   never needs to evict old values, this is smaller and faster than
+   :func:`lru_cache()` with a size limit.
+
+   For example::
+
+        @cache
+        def factorial(n):
+            return n * factorial(n-1) if n else 1
+
+        >>> factorial(10)      # no previously cached result, makes 11 recursive calls
+        3628800
+        >>> factorial(5)       # just looks up cached value result
+        120
+        >>> factorial(12)      # makes two new recursive calls, the other 10 are cached
+        479001600
+
+   .. versionadded:: 3.9
+
+
 .. decorator:: cached_property(func)
 
    Transform a method of a class into a property whose value is computed once
diff --git a/Lib/functools.py b/Lib/functools.py
index f05b106b62c00..87c7d87438998 100644
--- a/Lib/functools.py
+++ b/Lib/functools.py
@@ -10,7 +10,7 @@
 # See C source code for _functools credits/copyright
 
 __all__ = ['update_wrapper', 'wraps', 'WRAPPER_ASSIGNMENTS', 'WRAPPER_UPDATES',
-           'total_ordering', 'cmp_to_key', 'lru_cache', 'reduce',
+           'total_ordering', 'cache', 'cmp_to_key', 'lru_cache', 'reduce',
            'TopologicalSorter', 'CycleError',
            'partial', 'partialmethod', 'singledispatch', 'singledispatchmethod',
            'cached_property']
@@ -888,6 +888,15 @@ def cache_clear():
     pass
 
 
+################################################################################
+### cache -- simplified access to the infinity cache
+################################################################################
+
+def cache(user_function, /):
+    'Simple lightweight unbounded cache.  Sometimes called "memoize".'
+    return lru_cache(maxsize=None)(user_function)
+
+
 ################################################################################
 ### singledispatch() - single-dispatch generic function decorator
 ################################################################################
diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py
index b3893a15566fa..e122fe0b33340 100644
--- a/Lib/test/test_functools.py
+++ b/Lib/test/test_functools.py
@@ -1432,6 +1432,25 @@ def check_order_with_hash_seed(seed):
         self.assertEqual(run1, run2)
 
 
+class TestCache:
+    # This tests that the pass-through is working as designed.
+    # The underlying functionality is tested in TestLRU.
+
+    def test_cache(self):
+        @self.module.cache
+        def fib(n):
+            if n < 2:
+                return n
+            return fib(n-1) + fib(n-2)
+        self.assertEqual([fib(n) for n in range(16)],
+            [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610])
+        self.assertEqual(fib.cache_info(),
+            self.module._CacheInfo(hits=28, misses=16, maxsize=None, currsize=16))
+        fib.cache_clear()
+        self.assertEqual(fib.cache_info(),
+            self.module._CacheInfo(hits=0, misses=0, maxsize=None, currsize=0))
+
+
 class TestLRU:
 
     def test_lru(self):
diff --git a/Misc/NEWS.d/next/Library/2020-05-09-15-38-25.bpo-40571.kOXZGC.rst b/Misc/NEWS.d/next/Library/2020-05-09-15-38-25.bpo-40571.kOXZGC.rst
new file mode 100644
index 0000000000000..476770f6974d2
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2020-05-09-15-38-25.bpo-40571.kOXZGC.rst
@@ -0,0 +1,2 @@
+Added functools.cache() as a simpler, more discoverable way to access the
+unbounded cache variant of lru_cache(maxsize=None).



More information about the Python-checkins mailing list