This rule raises an issue when an instance method is decorated with functools.lru_cache or functools.cache.

Why is this an issue?

functools.lru_cache and functools.cache store call results in a process-wide cache keyed by the function arguments. On an instance method, self is part of that key.

The cache therefore keeps a strong reference to every instance that was used as a receiver. Those instances and anything they reference cannot be garbage-collected for as long as the cache entry lives. The instances lifetimes are coupled to a global memoization table. With an unbounded @cache, or a large @lru_cache(maxsize=…​), memory use grows with the number of distinct instances.

Caching couples the lifetime of business objects to a global memoization table.

What is the potential impact?

Exceptions

Do not raise on @staticmethod or @classmethod methods: there is no per-instance self retained by the cache.

Do not raise on methods of enum.Enum subclasses: enum members are singletons, so retaining them is intentional and bounded.

How to fix it

Move the cache to a free function that takes only the values needed for the computation, then call that function from the method. Alternatively, cache on a pure helper that does not close over self, or use an instance-local memo if the lifetime must match the object.

Code examples

Noncompliant code example

from functools import lru_cache

class Number:
    def __init__(self, value: int) -> None:
        self.value = value

    @lru_cache  # Noncompliant: cache retains "self"
    def squared(self) -> int:
        return self.value * self.value

Compliant solution

from functools import lru_cache

@lru_cache
def square(value: int) -> int:
    return value * value

class Number:
    def __init__(self, value: int) -> None:
        self.value = value

    def squared(self) -> int:
        return square(self.value)

Resources

Documentation

Articles & blog posts

External coding guidelines