mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 07:31:38 +00:00 
			
		
		
		
	Issue 13227: Option to make the lru_cache() type specific (suggested by Andrew Koenig).
This commit is contained in:
		
							parent
							
								
									e3455c026a
								
							
						
					
					
						commit
						cd9fdfd652
					
				
					 5 changed files with 49 additions and 12 deletions
				
			
		| 
						 | 
					@ -40,7 +40,7 @@ The :mod:`functools` module defines the following functions:
 | 
				
			||||||
   .. versionadded:: 3.2
 | 
					   .. 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
 | 
					   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
 | 
					   *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
 | 
					   If *maxsize* is set to None, the LRU feature is disabled and the cache
 | 
				
			||||||
   can grow without bound.
 | 
					   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*
 | 
					   To help measure the effectiveness of the cache and tune the *maxsize*
 | 
				
			||||||
   parameter, the wrapped function is instrumented with a :func:`cache_info`
 | 
					   parameter, the wrapped function is instrumented with a :func:`cache_info`
 | 
				
			||||||
   function that returns a :term:`named tuple` showing *hits*, *misses*,
 | 
					   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
 | 
					   An `LRU (least recently used) cache
 | 
				
			||||||
   <http://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used>`_ works
 | 
					   <http://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used>`_ works
 | 
				
			||||||
   best when more recent calls are the best predictors of upcoming calls (for
 | 
					   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 daily).
 | 
					   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
 | 
					   The cache's size limit assures that the cache does not grow without bound on
 | 
				
			||||||
   long-running processes such as web servers.
 | 
					   long-running processes such as web servers.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -111,6 +115,9 @@ The :mod:`functools` module defines the following functions:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   .. versionadded:: 3.2
 | 
					   .. versionadded:: 3.2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   .. versionchanged:: 3.3
 | 
				
			||||||
 | 
					      Added the *typed* option.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. decorator:: total_ordering
 | 
					.. decorator:: total_ordering
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   Given a class defining one or more rich comparison ordering methods, this
 | 
					   Given a class defining one or more rich comparison ordering methods, this
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -121,12 +121,16 @@ def __ne__(self, other):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
_CacheInfo = namedtuple("CacheInfo", "hits misses maxsize currsize")
 | 
					_CacheInfo = namedtuple("CacheInfo", "hits misses maxsize currsize")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def lru_cache(maxsize=100):
 | 
					def lru_cache(maxsize=100, typed=False):
 | 
				
			||||||
    """Least-recently-used cache decorator.
 | 
					    """Least-recently-used cache decorator.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    If *maxsize* is set to None, the LRU features are disabled and the cache
 | 
					    If *maxsize* is set to None, the LRU features are disabled and the cache
 | 
				
			||||||
    can grow without bound.
 | 
					    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.
 | 
					    Arguments to the cached function must be hashable.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    View the cache statistics named tuple (hits, misses, maxsize, currsize) with
 | 
					    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).
 | 
					    # to allow the implementation to change (including a possible C version).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def decorating_function(user_function,
 | 
					    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
 | 
					        hits = misses = 0
 | 
				
			||||||
        kwd_mark = (object(),)          # separates positional and keyword args
 | 
					        kwd_mark = (object(),)          # separates positional and keyword args
 | 
				
			||||||
| 
						 | 
					@ -156,7 +160,12 @@ def wrapper(*args, **kwds):
 | 
				
			||||||
                nonlocal hits, misses
 | 
					                nonlocal hits, misses
 | 
				
			||||||
                key = args
 | 
					                key = args
 | 
				
			||||||
                if kwds:
 | 
					                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:
 | 
					                try:
 | 
				
			||||||
                    result = cache[key]
 | 
					                    result = cache[key]
 | 
				
			||||||
                    hits += 1
 | 
					                    hits += 1
 | 
				
			||||||
| 
						 | 
					@ -177,7 +186,12 @@ def wrapper(*args, **kwds):
 | 
				
			||||||
                nonlocal hits, misses
 | 
					                nonlocal hits, misses
 | 
				
			||||||
                key = args
 | 
					                key = args
 | 
				
			||||||
                if kwds:
 | 
					                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:
 | 
					                with lock:
 | 
				
			||||||
                    try:
 | 
					                    try:
 | 
				
			||||||
                        result = cache[key]
 | 
					                        result = cache[key]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -207,7 +207,7 @@ def compile(pattern, flags=0):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def purge():
 | 
					def purge():
 | 
				
			||||||
    "Clear the regular expression caches"
 | 
					    "Clear the regular expression caches"
 | 
				
			||||||
    _compile_typed.cache_clear()
 | 
					    _compile.cache_clear()
 | 
				
			||||||
    _compile_repl.cache_clear()
 | 
					    _compile_repl.cache_clear()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def template(pattern, flags=0):
 | 
					def template(pattern, flags=0):
 | 
				
			||||||
| 
						 | 
					@ -253,11 +253,8 @@ def escape(pattern):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
_pattern_type = type(sre_compile.compile("", 0))
 | 
					_pattern_type = type(sre_compile.compile("", 0))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@functools.lru_cache(maxsize=500, typed=True)
 | 
				
			||||||
def _compile(pattern, flags):
 | 
					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
 | 
					    # internal: compile pattern
 | 
				
			||||||
    if isinstance(pattern, _pattern_type):
 | 
					    if isinstance(pattern, _pattern_type):
 | 
				
			||||||
        if flags:
 | 
					        if flags:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -734,6 +734,22 @@ def func(i):
 | 
				
			||||||
            with self.assertRaises(IndexError):
 | 
					            with self.assertRaises(IndexError):
 | 
				
			||||||
                func(15)
 | 
					                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):
 | 
					def test_main(verbose=None):
 | 
				
			||||||
    test_classes = (
 | 
					    test_classes = (
 | 
				
			||||||
        TestPartial,
 | 
					        TestPartial,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -319,6 +319,9 @@ Core and Builtins
 | 
				
			||||||
Library
 | 
					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
 | 
					- Issue #6090: zipfile raises a ValueError when a document with a timestamp
 | 
				
			||||||
  earlier than 1980 is provided. Patch contributed by Petri Lehtinen.
 | 
					  earlier than 1980 is provided. Patch contributed by Petri Lehtinen.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue