mirror of https://github.com/python/cpython
Issue 13227: Option to make the lru_cache() type specific (suggested by Andrew Koenig).
This commit is contained in:
parent
e3455c026a
commit
cd9fdfd652
|
@ -40,7 +40,7 @@ The :mod:`functools` module defines the following functions:
|
|||
.. versionadded:: 3.2
|
||||
|
||||
|
||||
.. decorator:: lru_cache(maxsize=100)
|
||||
.. decorator:: lru_cache(maxsize=100, 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
|
||||
|
@ -52,6 +52,10 @@ The :mod:`functools` module defines the following functions:
|
|||
If *maxsize* is set to None, the LRU feature is disabled and the cache
|
||||
can grow without bound.
|
||||
|
||||
If *typed* is set to True, function arguments of different types will be
|
||||
cached separately. For example, ``f(3)`` and ``f(3.0)`` will be treated
|
||||
as distinct calls with distinct results.
|
||||
|
||||
To help measure the effectiveness of the cache and tune the *maxsize*
|
||||
parameter, the wrapped function is instrumented with a :func:`cache_info`
|
||||
function that returns a :term:`named tuple` showing *hits*, *misses*,
|
||||
|
@ -67,8 +71,8 @@ The :mod:`functools` module defines the following functions:
|
|||
|
||||
An `LRU (least recently used) cache
|
||||
<http://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used>`_ works
|
||||
best when more recent calls are the best predictors of upcoming calls (for
|
||||
example, the most popular articles on a news server tend to change daily).
|
||||
best when the most recent calls are the best predictors of upcoming calls (for
|
||||
example, the most popular articles on a news server tend to change each day).
|
||||
The cache's size limit assures that the cache does not grow without bound on
|
||||
long-running processes such as web servers.
|
||||
|
||||
|
@ -111,6 +115,9 @@ The :mod:`functools` module defines the following functions:
|
|||
|
||||
.. versionadded:: 3.2
|
||||
|
||||
.. versionchanged:: 3.3
|
||||
Added the *typed* option.
|
||||
|
||||
.. decorator:: total_ordering
|
||||
|
||||
Given a class defining one or more rich comparison ordering methods, this
|
||||
|
|
|
@ -121,12 +121,16 @@ except ImportError:
|
|||
|
||||
_CacheInfo = namedtuple("CacheInfo", "hits misses maxsize currsize")
|
||||
|
||||
def lru_cache(maxsize=100):
|
||||
def lru_cache(maxsize=100, typed=False):
|
||||
"""Least-recently-used cache decorator.
|
||||
|
||||
If *maxsize* is set to None, the LRU features are disabled and the cache
|
||||
can grow without bound.
|
||||
|
||||
If *typed* is True, arguments of different types will be cached separately.
|
||||
For example, f(3.0) and f(3) will be treated as distinct calls with
|
||||
distinct results.
|
||||
|
||||
Arguments to the cached function must be hashable.
|
||||
|
||||
View the cache statistics named tuple (hits, misses, maxsize, currsize) with
|
||||
|
@ -142,7 +146,7 @@ def lru_cache(maxsize=100):
|
|||
# to allow the implementation to change (including a possible C version).
|
||||
|
||||
def decorating_function(user_function,
|
||||
tuple=tuple, sorted=sorted, len=len, KeyError=KeyError):
|
||||
*, tuple=tuple, sorted=sorted, map=map, len=len, type=type, KeyError=KeyError):
|
||||
|
||||
hits = misses = 0
|
||||
kwd_mark = (object(),) # separates positional and keyword args
|
||||
|
@ -156,7 +160,12 @@ def lru_cache(maxsize=100):
|
|||
nonlocal hits, misses
|
||||
key = args
|
||||
if kwds:
|
||||
key += kwd_mark + tuple(sorted(kwds.items()))
|
||||
sorted_items = tuple(sorted(kwds.items()))
|
||||
key += kwd_mark + sorted_items
|
||||
if typed:
|
||||
key += tuple(map(type, args))
|
||||
if kwds:
|
||||
key += tuple(type(v) for k, v in sorted_items)
|
||||
try:
|
||||
result = cache[key]
|
||||
hits += 1
|
||||
|
@ -177,7 +186,12 @@ def lru_cache(maxsize=100):
|
|||
nonlocal hits, misses
|
||||
key = args
|
||||
if kwds:
|
||||
key += kwd_mark + tuple(sorted(kwds.items()))
|
||||
sorted_items = tuple(sorted(kwds.items()))
|
||||
key += kwd_mark + sorted_items
|
||||
if typed:
|
||||
key += tuple(map(type, args))
|
||||
if kwds:
|
||||
key += tuple(type(v) for k, v in sorted_items)
|
||||
with lock:
|
||||
try:
|
||||
result = cache[key]
|
||||
|
|
|
@ -207,7 +207,7 @@ def compile(pattern, flags=0):
|
|||
|
||||
def purge():
|
||||
"Clear the regular expression caches"
|
||||
_compile_typed.cache_clear()
|
||||
_compile.cache_clear()
|
||||
_compile_repl.cache_clear()
|
||||
|
||||
def template(pattern, flags=0):
|
||||
|
@ -253,11 +253,8 @@ def escape(pattern):
|
|||
|
||||
_pattern_type = type(sre_compile.compile("", 0))
|
||||
|
||||
@functools.lru_cache(maxsize=500, typed=True)
|
||||
def _compile(pattern, flags):
|
||||
return _compile_typed(type(pattern), pattern, flags)
|
||||
|
||||
@functools.lru_cache(maxsize=500)
|
||||
def _compile_typed(text_bytes_type, pattern, flags):
|
||||
# internal: compile pattern
|
||||
if isinstance(pattern, _pattern_type):
|
||||
if flags:
|
||||
|
|
|
@ -734,6 +734,22 @@ class TestLRU(unittest.TestCase):
|
|||
with self.assertRaises(IndexError):
|
||||
func(15)
|
||||
|
||||
def test_lru_with_types(self):
|
||||
for maxsize in (None, 100):
|
||||
@functools.lru_cache(maxsize=maxsize, typed=True)
|
||||
def square(x):
|
||||
return x * x
|
||||
self.assertEqual(square(3), 9)
|
||||
self.assertEqual(type(square(3)), type(9))
|
||||
self.assertEqual(square(3.0), 9.0)
|
||||
self.assertEqual(type(square(3.0)), type(9.0))
|
||||
self.assertEqual(square(x=3), 9)
|
||||
self.assertEqual(type(square(x=3)), type(9))
|
||||
self.assertEqual(square(x=3.0), 9.0)
|
||||
self.assertEqual(type(square(x=3.0)), type(9.0))
|
||||
self.assertEqual(square.cache_info().hits, 4)
|
||||
self.assertEqual(square.cache_info().misses, 4)
|
||||
|
||||
def test_main(verbose=None):
|
||||
test_classes = (
|
||||
TestPartial,
|
||||
|
|
|
@ -319,6 +319,9 @@ Core and Builtins
|
|||
Library
|
||||
-------
|
||||
|
||||
- Issue #13227: functools.lru_cache() now has a option to distinguish
|
||||
calls with different argument types.
|
||||
|
||||
- Issue #6090: zipfile raises a ValueError when a document with a timestamp
|
||||
earlier than 1980 is provided. Patch contributed by Petri Lehtinen.
|
||||
|
||||
|
|
Loading…
Reference in New Issue