Python Decorator to Memoize Instance Methods
For the life of me I could not find a python decorator that memoizes methods on an instance. Something that replaces the pattern:
class A:
def x(self):
if self._x is not None:
return self._x
self._x = something_difficult()
return self._x
Sure there is functools
@cache
and @cached_property
but these use a global lru_cache
for everything. I want the cache to live and die with the instance.
I am still pretty new to python, but slowly getting the hang of it. That being said I came up with this:
class memoize:
def __init__(self, func):
self.func = func
fargspec = inspect.getfullargspec(func)
if len(fargspec.args) != 1 or fargspec.args[0] != "self":
raise "@memoize must be `(self)`"
# set key for this function
self.func_key = str(func)def __get__(self, instance, cls):
if instance is None:
raise "@memoize's must be bound"
# Set the cache
if not hasattr(instance, "_memoize_cache"):
setattr(instance, "_memoize_cache", {})
# return function bound to instance
return types.MethodType(self, instance)
def __call__(self, *args, **kwargs):
# args[0] will always be self
instance = args[0]
cache = instance._memoize_cache
# return if cached
if self.func_key in cache:
return cache[self.func_key]
# calculate and cache
result = self.func(*args, **kwargs)
cache[self.func_key] = result
return result
The quick breakdown of the memoize decorator is:
__init__
first makes sure the method has signature(self)
and create thefunc_key
for the cache__get__
creates the instance cache ifNone
then returns the bound function__call__
gets the instance cache, returns value if function already cached, otherwise calls the wrapped function and caches the result
And now the function looks much nicer:
class A:
@memoize
def x(self):
return something_difficult()
This might be a bad idea for some reason (given that I couldn’t find anyone implementing this). Friendly comments are welcome :)