initial version of the cacheutils cached decorator

This commit is contained in:
Mahmoud Hashemi 2015-04-04 16:44:45 -07:00
parent e7bc040a15
commit 219c109242
1 changed files with 73 additions and 0 deletions

View File

@ -52,8 +52,10 @@ except:
try:
from compat import make_sentinel
_MISSING = make_sentinel(var_name='_MISSING')
_KWARG_MARK = make_sentinel(var_name='_KWARG_MARK')
except ImportError:
_MISSING = object()
_KWARG_MARK = object()
PREV, NEXT, KEY, VALUE = range(4) # names for the link fields
@ -322,6 +324,77 @@ class LRI(dict):
return default
### Cached decorator
# Key-making technique adapted from Python 3.4's functools
class _HashedKey(list):
"""The _HashedKey guarantees that hash() will be called no more than once
per cached function invocation.
"""
__slots__ = 'hash_value'
def __init__(self, key):
self[:] = key
self.hash_value = hash(tuple(key))
def __hash__(self):
return self.hash_value
def _make_cache_key(args, kwargs, typed=False, kwarg_mark=_KWARG_MARK,
fasttypes=frozenset(int, str, frozenset, type(None))):
"""Make a cache key from optionally typed positional and keyword
arguments. If *typed* is ``True``, ``3`` and ``3.0`` will be
treated as separate keys.
The key is constructed in a way that is flat as possible rather than
as a nested structure that would take more memory.
If there is only a single argument and its data type is known to cache
its hash value, then that argument is returned without a wrapper. This
saves space and improves lookup speed.
"""
key = list(args)
if kwargs:
sorted_items = sorted(kwargs.items())
key.append(kwarg_mark)
key.extend(sorted_items)
if typed:
key.extend([type(v) for v in args])
if kwargs:
key.extend([type(v) for k, v in sorted_items])
elif len(key) == 1 and type(key[0]) in fasttypes:
return key[0]
return _HashedKey(key)
class CachedFunction(object):
def __init__(self, func, cache, typed=False):
self.func = func
self.cache = cache
self.typed = typed
def __call__(self, *args, **kwargs):
key = _make_cache_key(args, kwargs, typed=self.typed)
try:
ret = self.cache[key]
except KeyError:
ret = self.cache[key] = self.func(*args, **kwargs)
return ret
def __repr__(self):
cn = self.__class__.__name__
if self.typed:
return "%s(func=%r, typed=%r)" % (cn, self.func, self.typed)
return "%s(func=%r)" % (cn, self.func)
def cached(cache, typed=False):
def cached_func_decorator(func):
return CachedFunction(func, cache, typed=typed)
if __name__ == '__main__':
def _test_lri():
import string