I am using the memoize recipe from here and have slightly modified it for a function that returns 2 values. I use this wrapper to create two separate functions that returns the first and the second value individually but the function evaluations are cached so that there is no overhead when either of the returned functions are called with the same argument. Here is the code for this wrapper.
def memoize(obj, cache_limit=10):
'''
This function caches the return value each time it is called. partial() is used to return the appropriate value.
Cache size is limited to 10
See here for details on this design pattern: https://wiki.python.org/moin/PythonDecoratorLibrary#Memoize
'''
cache = obj.cache = {}
key_cache = collections.deque()
@functools.wraps(obj)
def memoizer(which, *args, **kwargs):
key = str(args)
if key not in cache:
cache[key] = obj(*args, **kwargs)
key_cache.append(key)
if len(key_cache) >= cache_limit:
del cache[key_cache.popleft()]
return cache[key][which]
return functools.partial(memoizer, 0), functools.partial(memoizer, 1)
Now, I am trying to use this on function f
which is defined in a class this way:
class test_function:
def __init__(self):
''''''
def f(self,x):
return 2*x, 3*x
And I call it this way
a = test_function()
f_v1, f_v2 = memoize(a.f)
If successful f_v1(x)
would return 2x
and f_v2(x)
would return 3x
. But this fails with an error:
AttributeError: 'instancemethod' object has no attribute 'cache'
My code works fine if the function is declared outside of the class. What am I missing? I am using Python 2.7
.
Methods are a different sort of object than functions (an instancemethod
object, as the error message suggests; this type is available as types.MethodType
). Unlike function objects, instance methods don't have a __dict__
, so you can't set arbitrary attributes on them; you can't do obj.someMethod.someAttribute = "blah"
to create your own custom attribute called someAttribute
on the method.
It's not clear to me why you are storing the cache on the object anyway, since you never actually access it from there. If you just use the local variable cache
, it will be saved in a closure and will work fine:
def memoize(obj, cache_limit=10):
'''
This function caches the return value each time it is called. partial() is used to return the appropriate value.
Cache size is limited to 10
See here for details on this design pattern: https://wiki.python.org/moin/PythonDecoratorLibrary#Memoize
'''
cache = {}
key_cache = collections.deque()
@functools.wraps(obj)
def memoizer(which, *args, **kwargs):
key = str(args)
if key not in cache:
cache[key] = obj(*args, **kwargs)
key_cache.append(key)
if len(key_cache) >= cache_limit:
del cache[key_cache.popleft()]
return cache[key][which]
return functools.partial(memoizer, 0), functools.partial(memoizer, 1)
>>> a = test_function()
... f_v1, f_v2 = memoize(a.f)
>>> f_v1(2)
4
>>> f_v2(2)
6