Search code examples
pythoncachingidentity

Can I use Python's functools @cache based on identity?


I would like to have a Python @cache decorator based on identity, not __hash__/__equal.

That is to say, I would like the cached value for an argument ka NOT to be used for a different object ka2, even if ka == ka2.

Is there a way to do that?

In code:

from functools import cache


class Key:
    def __init__(self, value):
        self.value = value

    def __eq__(self, another):
        print(f"__eq__ {self.value}, {another.value}")
        return another.value == self.value

    def __hash__(self):
        print(f"__hash__ {self.value}")
        return hash(self.value)

    def __repr__(self):
        return self.value


i = 0


@cache
def foo(key):
    global i
    i += 1
    print(f"Computing foo({key}) = {i}")
    return i


ka = Key('a')
ka2 = Key('a')

print(f"foo(ka): {foo(ka)}")
print(f"foo(ka2): {foo(ka2)}")  # I would like the cached value for ka NOT to be used even though ka2 == ka.

Solution

  • Make a wrapper like Key that compares by the identity of its wrapped object, and wrap your caching function in a helper that uses the wrapper:

    class Id:
      __slots__="x",
      def __init__(self,x): self.x=x
      def __hash__(self): return id(self.x)
      def __eq__(self,o): return self.x is o.x
    
    def cache_id(f):
      @functools.cache
      def id_f(i): return f(i.x)
      @functools.wraps(f)
      def call(x): return id_f(Id(x))
      return call
    
    @cache_id
    def foo(key): …