bpo-36772 Allow lru_cache to be used as decorator without making a function call (GH-13048)
This commit is contained in:
parent
aaf47caf35
commit
b821868e6d
|
@ -76,7 +76,8 @@ The :mod:`functools` module defines the following functions:
|
|||
.. versionadded:: 3.2
|
||||
|
||||
|
||||
.. decorator:: lru_cache(maxsize=128, typed=False)
|
||||
.. decorator:: lru_cache(user_function)
|
||||
lru_cache(maxsize=128, typed=False)
|
||||
|
||||
Decorator to wrap a function with a memoizing callable that saves up to the
|
||||
*maxsize* most recent calls. It can save time when an expensive or I/O bound
|
||||
|
@ -90,6 +91,15 @@ The :mod:`functools` module defines the following functions:
|
|||
differ in their keyword argument order and may have two separate cache
|
||||
entries.
|
||||
|
||||
If *user_function* is specified, it must be a callable. This allows the
|
||||
*lru_cache* decorator to be applied directly to a user function, leaving
|
||||
the *maxsize* at its default value of 128::
|
||||
|
||||
@lru_cache
|
||||
def count_vowels(sentence):
|
||||
sentence = sentence.casefold()
|
||||
return sum(sentence.count(vowel) for vowel in 'aeiou')
|
||||
|
||||
If *maxsize* is set to ``None``, the LRU feature is disabled and the cache can
|
||||
grow without bound. The LRU feature performs best when *maxsize* is a
|
||||
power-of-two.
|
||||
|
@ -165,6 +175,9 @@ The :mod:`functools` module defines the following functions:
|
|||
.. versionchanged:: 3.3
|
||||
Added the *typed* option.
|
||||
|
||||
.. versionchanged:: 3.8
|
||||
Added the *user_function* option.
|
||||
|
||||
.. decorator:: total_ordering
|
||||
|
||||
Given a class defining one or more rich comparison ordering methods, this
|
||||
|
|
|
@ -291,6 +291,23 @@ where the DLL is stored (if a full or partial path is used to load the initial
|
|||
DLL) and paths added by :func:`~os.add_dll_directory`.
|
||||
|
||||
|
||||
functools
|
||||
---------
|
||||
|
||||
:func:`functools.lru_cache` can now be used as a straight decorator rather
|
||||
than as a function returning a decorator. So both of these are now supported::
|
||||
|
||||
@lru_cache
|
||||
def f(x):
|
||||
...
|
||||
|
||||
@lru_cache(maxsize=256)
|
||||
def f(x):
|
||||
...
|
||||
|
||||
(Contributed by Raymond Hettinger in :issue:`36772`.)
|
||||
|
||||
|
||||
datetime
|
||||
--------
|
||||
|
||||
|
|
|
@ -518,14 +518,18 @@ def lru_cache(maxsize=128, typed=False):
|
|||
# The internals of the lru_cache are encapsulated for thread safety and
|
||||
# to allow the implementation to change (including a possible C version).
|
||||
|
||||
# Early detection of an erroneous call to @lru_cache without any arguments
|
||||
# resulting in the inner function being passed to maxsize instead of an
|
||||
# integer or None. Negative maxsize is treated as 0.
|
||||
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)
|
||||
return update_wrapper(wrapper, user_function)
|
||||
elif maxsize is not None:
|
||||
raise TypeError('Expected maxsize to be an integer or 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)
|
||||
|
|
|
@ -1251,6 +1251,18 @@ class TestLRU:
|
|||
self.assertEqual(misses, 4)
|
||||
self.assertEqual(currsize, 2)
|
||||
|
||||
def test_lru_no_args(self):
|
||||
@self.module.lru_cache
|
||||
def square(x):
|
||||
return x ** 2
|
||||
|
||||
self.assertEqual(list(map(square, [10, 20, 10])),
|
||||
[100, 400, 100])
|
||||
self.assertEqual(square.cache_info().hits, 1)
|
||||
self.assertEqual(square.cache_info().misses, 2)
|
||||
self.assertEqual(square.cache_info().maxsize, 128)
|
||||
self.assertEqual(square.cache_info().currsize, 2)
|
||||
|
||||
def test_lru_bug_35780(self):
|
||||
# C version of the lru_cache was not checking to see if
|
||||
# the user function call has already modified the cache
|
||||
|
@ -1582,13 +1594,6 @@ class TestLRU:
|
|||
self.assertEqual(test_func(DoubleEq(2)), # Trigger a re-entrant __eq__ call
|
||||
DoubleEq(2)) # Verify the correct return value
|
||||
|
||||
def test_early_detection_of_bad_call(self):
|
||||
# Issue #22184
|
||||
with self.assertRaises(TypeError):
|
||||
@functools.lru_cache
|
||||
def f():
|
||||
pass
|
||||
|
||||
def test_lru_method(self):
|
||||
class X(int):
|
||||
f_cnt = 0
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
functools.lru_cache() can now be used as a straight decorator in
|
||||
addition to its existing usage as a function that returns a decorator.
|
Loading…
Reference in New Issue