Unable to use lru_cache not as decorator
LukeMondy opened this issue · comments
Hello,
I'm having a little trouble trying to use the lru_cache not as a decorator.
So my goal is to allow a user to be allowed to supply a function to class, and still be cached. The functools.lru_cache
allows this behavior, but methodtools
comes up with an issue.
I've got some demo code here:
# A.py
from methodtools import lru_cache
class A(object):
def __init__(self, alternate_function = None):
if alternate_function:
self.func_to_use = alternate_function
else:
self.func_to_use = self.cached_method
@lru_cache(maxsize=100)
def cached_method(self, args):
return args
def use_method(self, args):
results = self.func_to_use(args)
print(results)
and
# B.py
from methodtools import lru_cache
class B(object):
def __init__(self, alternate_function = None):
if alternate_function:
self.func_to_use = alternate_function
else:
self.func_to_use = self.cached_method
# We apply the cache here, not as a decorator
self.func_to_use = lru_cache(maxsize=100)(self.func_to_use)
def cached_method(self, args):
return args
def use_method(self, args):
results = self.func_to_use(args)
print(results)
which is then called via:
# test.py
from A import A
from B import B
# Use the default function, which has an lru_cache decorator
objA = A()
objA.use_method("1")
objA.use_method("2")
print(objA.func_to_use.cache_info())
# define an alternative function
def alt(args):
return "woof: " + args
# Now the alternative function is called, but is not cached
objWoof = A(alternate_function = alt)
objWoof.use_method("1")
objWoof.use_method("2")
# Now we try to use class B, which tries to use the lru_cache not as a decorator
objB = B()
objB.use_method("1")
objB.use_method("2")
print(objB.func_to_use.cache_info())
def alt_meow(args):
return "meow: " + args
# Now the alternative function is not cached
objBWoof = B(alternate_function = alt_meow)
objBWoof.use_method("1")
objBWoof.use_method("2")
If you run all that, you get:
python test.py
1
2
CacheInfo(hits=0, misses=2, maxsize=100, currsize=2)
woof: 1
woof: 2
Traceback (most recent call last):
File "test.py", line 20, in <module>
objB = B()
File "/live/share/B.py", line 12, in __init__
self.func_to_use = lru_cache(maxsize=100)(self.func_to_use)
File "/usr/local/lib/python3.5/dist-packages/wirerope/rope.py", line 121, in __call__
cw = Callable(function)
File "/usr/local/lib/python3.5/dist-packages/wirerope/callable.py", line 108, in __init__
f = getattr(f, self.descriptor.detect_function_attr_name())
File "/usr/local/lib/python3.5/dist-packages/wirerope/callable.py", line 49, in detect_function_attr_name
descriptor = self.descriptor_class(indicator)
TypeError: method expected 2 arguments, got 1
If you swap methodtools
for functools
in B.py
, then the code works as expected. The trouble is that the functools
way runs into pickling issues.
Are you able to provide any insight?
Thank you for reporting interesting use case. The hacky part of method detection is suspicious. Let me look into it.
Here is a new release with the fix: https://pypi.org/project/methodtools/0.4.0/
Great, thanks so much!