123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105 |
- """Memoization utilities.
-
- """
-
- from functools import wraps
-
- from .core import ENOVAL
-
- def memoize(cache, name=None, typed=False, expire=None, tag=None):
- """Memoizing cache decorator.
-
- Decorator to wrap callable with memoizing function using cache. Repeated
- calls with the same arguments will lookup result in cache and avoid
- function evaluation.
-
- If name is set to None (default), the callable name will be determined
- automatically.
-
- 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.
-
- The original underlying function is accessible through the __wrapped__
- attribute. This is useful for introspection, for bypassing the cache, or
- for rewrapping the function with a different cache.
-
- >>> from diskcache import FanoutCache
- >>> cache = FanoutCache('/tmp/diskcache/fanoutcache')
- >>> @cache.memoize(typed=True, expire=1, tag='fib')
- ... def fibonacci(number):
- ... if number == 0:
- ... return 0
- ... elif number == 1:
- ... return 1
- ... else:
- ... return fibonacci(number - 1) + fibonacci(number - 2)
- >>> print(sum(fibonacci(number=value) for value in range(100)))
- 573147844013817084100
-
- Remember to call memoize when decorating a callable. If you forget, then a
- TypeError will occur. Note the lack of parenthenses after memoize below:
-
- >>> @cache.memoize
- ... def test():
- ... pass
- Traceback (most recent call last):
- ...
- TypeError: name cannot be callable
-
- :param cache: cache to store callable arguments and return values
- :param str name: name given for callable (default None, automatic)
- :param bool typed: cache different types separately (default False)
- :param float expire: seconds until arguments expire
- (default None, no expiry)
- :param str tag: text to associate with arguments (default None)
- :return: callable decorator
-
- """
- if callable(name):
- raise TypeError('name cannot be callable')
-
- def decorator(function):
- "Decorator created by memoize call for callable."
- if name is None:
- try:
- reference = function.__qualname__
- except AttributeError:
- reference = function.__name__
-
- reference = function.__module__ + reference
- else:
- reference = name
-
- reference = (reference,)
-
- @wraps(function)
- def wrapper(*args, **kwargs):
- "Wrapper for callable to cache arguments and return values."
-
- key = reference + args
-
- if kwargs:
- key += (ENOVAL,)
- sorted_items = sorted(kwargs.items())
-
- for item in sorted_items:
- key += item
-
- if typed:
- key += tuple(type(arg) for arg in args)
-
- if kwargs:
- key += tuple(type(value) for _, value in sorted_items)
-
- result = cache.get(key, default=ENOVAL, retry=True)
-
- if result is ENOVAL:
- result = function(*args, **kwargs)
- cache.set(key, result, expire=expire, tag=tag, retry=True)
-
- return result
-
- return wrapper
-
- return decorator
|