diff options
author | Raymond Hettinger <python@rcn.com> | 2012-05-01 05:32:16 (GMT) |
---|---|---|
committer | Raymond Hettinger <python@rcn.com> | 2012-05-01 05:32:16 (GMT) |
commit | 9acbb6074f201e8747a1be577d6a71571528243a (patch) | |
tree | 4e509d548da4357de85afef79bb0b2f920399ad2 /Lib/functools.py | |
parent | 018b4fbb9bab5d7aed513ee04845f25cd702e413 (diff) | |
download | cpython-9acbb6074f201e8747a1be577d6a71571528243a.zip cpython-9acbb6074f201e8747a1be577d6a71571528243a.tar.gz cpython-9acbb6074f201e8747a1be577d6a71571528243a.tar.bz2 |
Move make_key() out of the decorator body. Make keys that only need to be hashed once.
Diffstat (limited to 'Lib/functools.py')
-rw-r--r-- | Lib/functools.py | 44 |
1 files changed, 27 insertions, 17 deletions
diff --git a/Lib/functools.py b/Lib/functools.py index 66af5f3..8ef1732 100644 --- a/Lib/functools.py +++ b/Lib/functools.py @@ -142,6 +142,30 @@ except ImportError: _CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"]) +class _CacheKey(list): + 'Make a cache key from optionally typed positional and keyword arguments' + + __slots__ = 'hashvalue' + + def __init__(self, args, kwds, typed, + kwd_mark = (object(),), + sorted=sorted, tuple=tuple, type=type, hash=hash): + key = args + if kwds: + sorted_items = sorted(kwds.items()) + key += kwd_mark + for item in sorted_items: + key += item + if typed: + key += tuple(type(v) for v in args) + if kwds: + key += tuple(type(v) for k, v in sorted_items) + self[:] = key + self.hashvalue = hash(key) # so we only have to hash just once + + def __hash__(self): + return self.hashvalue + def lru_cache(maxsize=100, typed=False): """Least-recently-used cache decorator. @@ -168,8 +192,8 @@ def lru_cache(maxsize=100, typed=False): # to allow the implementation to change (including a possible C version). # Constants shared by all lru cache instances: - kwd_mark = (object(),) # separate positional and keyword args sentinel = object() # unique object used to signal cache misses + make_key = _CacheKey # build a key from the function arguments PREV, NEXT, KEY, RESULT = 0, 1, 2, 3 # names for the link fields def decorating_function(user_function): @@ -182,20 +206,6 @@ def lru_cache(maxsize=100, typed=False): root = [] # root of the circular doubly linked list root[:] = [root, root, None, None] # initialize by pointing to self - def make_key(args, kwds, typed, tuple=tuple, sorted=sorted, type=type): - # build a cache key from positional and keyword args - key = args - if kwds: - sorted_items = tuple(sorted(kwds.items())) - key += kwd_mark - key += tuple(k for k, v in sorted_items) - key += tuple(v for k, v in sorted_items) - if typed: - key += tuple(type(v) for v in args) - if kwds: - key += tuple(type(v) for k, v in sorted_items) - return key - if maxsize == 0: def wrapper(*args, **kwds): @@ -210,7 +220,7 @@ def lru_cache(maxsize=100, typed=False): def wrapper(*args, **kwds): # simple caching without ordering or size limit nonlocal hits, misses, currsize - key = make_key(args, kwds, typed) if kwds or typed else args + key = make_key(args, kwds, typed) result = cache_get(key, sentinel) if result is not sentinel: hits += 1 @@ -226,7 +236,7 @@ def lru_cache(maxsize=100, typed=False): def wrapper(*args, **kwds): # size limited caching that tracks accesses by recency nonlocal root, hits, misses, currsize, full - key = make_key(args, kwds, typed) if kwds or typed else args + key = make_key(args, kwds, typed) with lock: link = cache_get(key) if link is not None: |