Methodtools
Methodtools extends standard `functools` functionality to class methods, providing decorators like `cached_method`, `lru_method`, `weak_method`, and `locked_method`. It simplifies applying common patterns (caching, locking) directly to instance methods. It is currently at version 0.4.7 and has an active, consistent release cadence.
Warnings
- gotcha When combining `methodtools` decorators with `@property`, `@staticmethod`, or `@classmethod`, the order often matters. Generally, `methodtools` decorators should be applied *before* `@property` (i.e., `methodtools` decorator innermost). Incorrect order can lead to caching the property descriptor itself rather than its computed value.
- gotcha Like `functools.cache`, `cached_method` relies on argument hashability. If a method takes mutable arguments (e.g., lists, dictionaries) and their *contents* change after the first call, the cached result will be returned, not reflecting the changed input. This can lead to stale data.
- gotcha For `cached_method` and `lru_method`, the cache is managed per instance. To manually clear the cache for a specific instance's method, you need to access the underlying wrapped function. Direct attribute deletion won't work.
Install
-
pip install methodtools
Imports
- cached_method
from methodtools import cached_method
- lru_method
from methodtools import lru_method
- weak_method
from methodtools import weak_method
- locked_method
from methodtools import locked_method
Quickstart
from methodtools import cached_method
class MyClass:
def __init__(self, value):
self._value = value
self._compute_count = 0
@cached_method
def compute_something(self):
"""A method that computes something expensive."""
self._compute_count += 1
return self._value * 2
obj = MyClass(10)
print(f"First call: {obj.compute_something()}")
print(f"Second call (cached): {obj.compute_something()}")
print(f"Compute count (should be 1): {obj._compute_count}")
obj2 = MyClass(20)
print(f"First call for obj2: {obj2.compute_something()}")
print(f"Compute count for obj2 (should be 1): {obj2._compute_count}")