I am using Python 3.10. Consider this toy example of a cache that caches the very first call to an instance method and then returns the cached value on subsequent calls:
import functools
def cache(func):
@functools.wraps(func) # for __name__
def wrapper(*args, **kwargs):
if not wrapper.cache:
print("caching...")
wrapper.cache = func(*args, **kwargs)
return wrapper.cache
wrapper.cache = None
return wrapper
class Power:
def __init__(self, exponent):
self.exponent = exponent
@cache
def of(self, base):
return base ** self.exponent
# test
>>> cube = Power(3)
>>> cube.of(2)
caching...
8
>>> cube.of.cache
8
>>> cube.of.__dict__
{'__wrapped__': <function __main__.Power.of(self, base)>, 'cache': 8}
>>> cube.of.cache = None
...
AttributeError: 'method' object has no attribute 'cache'
I have two questions:
1.) The accepted answer here says that the @cache decorator runs when the Power class is constructed and it will be passed an unbound method (of in my case). I guess this claim is true only when you decorate an instance method with a class decorator. There it is an issue that you would need a reference of the cube object to be stored in the decorating class instance construction, but that cube instance is not defined yet. I am having trouble reconciling this claim with the fact that my example works; the decorated of method is passed a tuple with the first element being the cube instance and the second the base=2 parameter
2.) I can access the .cache attribute but why can't I reset it? It gives AttributeError.
What happens is that the decorating mechanism assigns the result of
cache(of)toPower.of. So it isPowerwhich has the attributeof(your cache decorator).You can write:
Power.of.cache = Noneto get what you want:Output: