I have a class A with three attributes a,b,c, where a is calculated from b and c (but this is expensive). Moreover, attributes b and c are likely to change over times. I want to make sure that:
the following code seems to work:
class A():
def __init__(self, b, c):
self._a = None
self._b = b
self._c = c
@property
def a(self):
if is None:
self.update_a()
return self._a
def update_a(self):
"""
compute a from b and c
"""
print('this is expensive')
self._a = self.b + 2*self.c
@property
def b(self):
return self._b
@b.setter
def b(self, value):
self._b = value
self._a = None #make sure a is recalculated before its next use
@property
def c(self):
return self._c
@c.setter
def c(self, value):
self._c = value
self._a = None #make sure a is recalculated before its next use
however this approach does not seem very good for many reasons:
Is there an abstract way to achieve this that does not require me to do all the bookkeeping myself? Ideally, I would like to have some sort of decorator which tells the property what its dependencies are so that all the bookkeeping happens under the hood.
I would like to write:
@cached_property_depends_on('b', 'c')
def a(self):
return self.b+2*self.c
or something like that.
EDIT: I would prefer solutions that do not require that the values assigned to a,b,c be immutable. I am mostly interested in np.arrays and lists but I would like the code to be reusable in many different situations without having to worry about mutability issues.
Implementing a Cache Using a Python Dictionary You can use the article's URL as the key and its content as the value. Save this code to a caching.py file, install the requests library, then run the script: $ pip install requests $ python caching.py Getting article... Fetching article from server...
cached_property is a decorator that converts a class method into a property whose value is calculated once and then cached like a regular attribute. The cached value will be available until the object or the instance of the class is destroyed.
lru_cache basics To memoize a function in Python, we can use a utility supplied in Python's standard library—the functools. lru_cache decorator. Now, every time you run the decorated function, lru_cache will check for a cached result for the inputs provided. If the result is in the cache, lru_cache will return it.
You could use functools.lru_cache
:
from functools import lru_cache
from operator import attrgetter
def cached_property_depends_on(*args):
attrs = attrgetter(*args)
def decorator(func):
_cache = lru_cache(maxsize=None)(lambda self, _: func(self))
def _with_tracked(self):
return _cache(self, attrs(self))
return property(_with_tracked, doc=func.__doc__)
return decorator
The idea is to retrieve the values of tracked attributes each time the property is accessed, pass them to the memoizing callable, but ignore them during the actual call.
Given a minimal implementation of the class:
class A:
def __init__(self, b, c):
self._b = b
self._c = c
@property
def b(self):
return self._b
@b.setter
def b(self, value):
self._b = value
@property
def c(self):
return self._c
@c.setter
def c(self, value):
self._c = value
@cached_property_depends_on('b', 'c')
def a(self):
print('Recomputing a')
return self.b + 2 * self.c
a = A(1, 1)
print(a.a)
print(a.a)
a.b = 3
print(a.a)
print(a.a)
a.c = 4
print(a.a)
print(a.a)
outputs
Recomputing a
3
3
Recomputing a
5
5
Recomputing a
11
11
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With