My class has a few regex methods that I would like to cache. I immediately reached my hand out to functools.cache
but I found out that it was destroying my original function signature which is a deal breaker for me. Is there something similar that also preserves the original function signature?
You can add it yourself:
new_func = functools.cache(func)
new_func.__signature__ = inspect.signature(func)
Can I use this as a decorator?
You can make your own decorator which wraps these 2 lines inside of it.
Let me know if you can not work out how, I will give you a hand.
Here’s what i’ve managed to come up with
from functools import lru_cache
from time import perf_counter, sleep
from typing import Callable, ParamSpec, TypeVar
T = TypeVar("T")
P = ParamSpec("P")
def cache(func: Callable[P, T], /) -> Callable[P, T]:
return lru_cache(maxsize=None)(func) # Incompatible return value type (got "_lru_cache_wrapper[T]", expected "Callable[P, T]") Mypy return-value
@cache
def expensive_function(a: int = 0, b: int | str | None = None) -> str:
sleep(10)
return "hello"
start = perf_counter()
result = expensive_function(20, None)
end = perf_counter()
print(end - start)
start = perf_counter()
result = expensive_function(20, None)
end = perf_counter()
print(end - start)
start = perf_counter()
result = expensive_function(20, None)
end = perf_counter()
print(end - start)
I’ve never typed a decorator before so I’m still unsure if what I’ve done is correct but it does manage to preserve the signature. (Thanks to this arcticle here.)
Although I’m unsure if my decorator is even wokring:
10.000129600000946
2.2999993234407157e-06
6.999998731771484e-07
How come the third call takes so much longer?
To me it seems to be A LOT faster. It is 7 / 10**7
.
I don’t use typing
so will not be of much help with that approach. Neither do I use mypy
. So maybe someone else can help you with this.
I would simply do it:
def cache(func):
wrapper = functools.cache(func)
wrapper.__signature__ = inspect.signature(func)
return wrapper
There has been a lot of discussions and multiple attempts at making lru_cache
preserve the typing signature, but this is hard because it also has to work with methods. See Use ParamSpec for lru_cache by cdce8p · Pull Request #11662 · python/typeshed · GitHub and related issues/PR.
For your more limited usecase, you can use a cast
or # type: ignore
to get the correct function signature.
Okay. Thanks alot everyone!