mirror of
https://github.com/wassname/catalyst.git
synced 2026-06-30 18:13:58 +08:00
58ceb7b7bb
- Moved zipline.utils.lazyval. - Added `remember_last` which is just `lru_cache(1)` with simpler logic.
102 lines
2.5 KiB
Python
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
|