What I meant by “depends on self” is that attributes of self are used in the function – and in that case, you need a way to check whether those attributes are the same in order to use the cached result. Usually that’s the hash function. If your class is immutable, I guess it doesn’t matter, but that’s a special case that doesn’t work in general.
class Mutable:
def __init__(self, x):
self.x = x
@functools.cache
def method(self, val):
return self.x + val
m = Mutable(5)
assert m.method(5) == m.x + 5
m.x = 10
assert m.method(5) == m.x + 5
...
AssertionError Traceback (most recent call last)
~/Junk/method_cache.py in <module>
19 m.x = 10
20
---> 21 assert m.method(5) == m.x + 5
22
23
AssertionError:
If the method doesn’t depend on any of the attributes in self, then why not make it a staticmethod?
NOTE: this actually brings up another gotcha with caching methods – if a method DOES depend on attributes of self, and an eq was not defined, then you won’t get any errors, but it will very much break when the instance is mutated
Probably deserves a note in the docs.

I’m not using caching to avoid computation cost, I’m using it to ensure that subsequent calls to the method return the same object as the first call.
Looking back – this is a special case – it’s pretty rare in Python to want the object, rather than the same value – and I think the general use case of caching is about values. So perhaps just write your own decorator to do this.
Another note: cache works by putting the arguments in a dict – which requires hashability – works great for immutable types: numbers, strings, etc. But not so much for others.
I suppose you could make a cache that was based on values – caching maybe using a stringifaction of the object, rather than the object itself as your key. And then the cache wouldn’t mess with garbage collection either.
It would only work for things that easily stringify, but it could be documented to work only for objects with an appropriate __repr__
Here’s a naive, quickie implkimentation – it works for at least some functions / values
def reprcache(func):
cache = {}
def __wrapped__(*args):
key = tuple(repr(arg) for arg in args)
if key in cache:
result = cache[key]
else:
result = func(*args)
cache[key] = result
return result
return __wrapped__
@reprcache
def a_func(x, y, z):
time.sleep(1) # to simulate slow function
return x + y + z
@reprcache
def a_mut_func(lst, x):
time.sleep(1) # to simulate slow function
return [x + i for i in lst]