Files
catalyst/zipline/utils/memoize.py
T
Scott Sanderson 58ceb7b7bb DEV: Add zipline.utils.memoize.
- Moved zipline.utils.lazyval.
- Added `remember_last` which is just `lru_cache(1)` with simpler logic.
2015-09-16 01:28:15 -04:00

102 lines
2.5 KiB
Python

"""
Tools for memoization of function results.
"""
from functools import wraps
from six import iteritems
from weakref import WeakKeyDictionary
class lazyval(object):
"""
Decorator that marks that an attribute should not be computed until
needed, and that the value should be memoized.
Example
-------
>>> from zipline.utils.memoize import lazyval
>>> class C(object):
... def __init__(self):
... self.count = 0
... @lazyval
... def val(self):
... self.count += 1
... return "val"
...
>>> c = C()
>>> c.count
0
>>> c.val, c.count
('val', 1)
>>> c.val, c.count
('val', 1)
"""
def __init__(self, get):
self._get = get
self._cache = WeakKeyDictionary()
def __get__(self, instance, owner):
if instance is None:
return self
try:
return self._cache[instance]
except KeyError:
self._cache[instance] = val = self._get(instance)
return val
def remember_last(f):
"""
Decorator that remembers the last computed value of a function and doesn't
recompute it when called with the same inputs multiple times.
Parameters
----------
f : The function to be memoized. All arguments to f should be hashable.
Example
-------
>>> counter = 0
>>> @remember_last
... def foo(x):
... global counter
... counter += 1
... return x, counter
>>> foo(1)
(1, 1)
>>> foo(1)
(1, 1)
>>> foo(0)
(0, 2)
>>> foo(1)
(1, 3)
Notes
-----
This decorator is equivalent to `lru_cache(1)` in Python 3, but with less
bells and whistles for handling things like threadsafety. If we ever
decide we need such bells and whistles, we should just make functools32 a
dependency.
"""
# This needs to be a mutable data structure so we can change it from inside
# the function. In pure Python 3, we'd use the nonlocal keyword for this.
_previous = [None, None]
KEY, VALUE = 0, 1
_kwd_mark = object()
@wraps(f)
def memoized_f(*args, **kwds):
# Hashing logic taken from functools32.lru_cache.
key = args
if kwds:
key += _kwd_mark + tuple(sorted(iteritems(kwds)))
key_hash = hash(key)
if key_hash != _previous[KEY]:
_previous[VALUE] = f(*args, **kwds)
_previous[KEY] = key_hash
return _previous[VALUE]
return memoized_f