]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-44782: Improve OrderedDict recipe for LRU cache variants (GH-27536)
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Mon, 2 Aug 2021 18:38:14 +0000 (11:38 -0700)
committerGitHub <noreply@github.com>
Mon, 2 Aug 2021 18:38:14 +0000 (11:38 -0700)
(cherry picked from commit 54f185b6d321a6354aef2b2886c766677f487ecb)

Co-authored-by: Raymond Hettinger <rhettinger@users.noreply.github.com>
Doc/library/collections.rst

index 2a0bc049f88dd51243cf9e03dbe78da3daa408c7..b1779a5b2382e57f63afcbdc8ecd520c7e619eae 100644 (file)
@@ -1171,27 +1171,43 @@ original insertion position is changed and moved to the end::
             self.move_to_end(key)
 
 An :class:`OrderedDict` would also be useful for implementing
-variants of :func:`functools.lru_cache`::
+variants of :func:`functools.lru_cache`:
 
-    class LRU(OrderedDict):
-        'Limit size, evicting the least recently looked-up key when full'
+.. testcode::
 
-        def __init__(self, maxsize=128, /, *args, **kwds):
-            self.maxsize = maxsize
-            super().__init__(*args, **kwds)
+    class LRU:
 
-        def __getitem__(self, key):
-            value = super().__getitem__(key)
-            self.move_to_end(key)
+        def __init__(self, func, maxsize=128):
+            self.func = func
+            self.maxsize = maxsize
+            self.cache = OrderedDict()
+
+        def __call__(self, *args):
+            if args in self.cache:
+                value = self.cache[args]
+                self.cache.move_to_end(args)
+                return value
+            value = self.func(*args)
+            if len(self.cache) >= self.maxsize:
+                self.cache.popitem(False)
+            self.cache[args] = value
             return value
 
-        def __setitem__(self, key, value):
-            if key in self:
-                self.move_to_end(key)
-            super().__setitem__(key, value)
-            if len(self) > self.maxsize:
-                oldest = next(iter(self))
-                del self[oldest]
+.. doctest::
+    :hide:
+
+    >>> def square(x):
+    ...     return x ** 2
+    ...
+    >>> s = LRU(square, maxsize=5)
+    >>> actual = [(s(x), s(x)) for x in range(20)]
+    >>> expected = [(x**2, x**2) for x in range(20)]
+    >>> actual == expected
+    True
+    >>> actual = list(s.cache.items())
+    >>> expected = [((x,), x**2) for x in range(15, 20)]
+    >>> actual == expected
+    True
 
 
 :class:`UserDict` objects