@lru_cache on functions with no arguments

Paul Moore p.f.moore at gmail.com
Thu Aug 3 10:35:28 EDT 2017


On Tuesday, 1 August 2017 15:54:42 UTC+1, t... at tomforb.es  wrote:
> > _sentinel = object()
> > _val = _sentinel
> > def val():
> >     if _val is _sentinel:
> >         # Calculate _val
> >     return _val
> > 
> > seems entirely sufficient for this case. Write a custom decorator if you use the idiom often enough to make it worth the effort.
> 
> I did some timings with this as part of my timings above and found it to be significantly slower than lru_cache with the C extension. I had to add `nonlocal` to get `_val` to resolve, which I think kills performance a bit.
> 
> I agree with the premise though, it might be worth exploring.

It's worth pointing out that there's nothing *wrong* with using lru_cache with maxsize=None. You're going to find it hard to get a pure-Python equivalent that's faster (after all, even maintaining a single variable is still a dict lookup, which is all the cache does when LRU functionality is disabled).

Certainly if you only ever call with no arguments there's little point in an LRU cache, so maxsize=None ("the LRU feature is disabled") is logical.

Whether that approach clearly expresses your intent is maybe a little less obvious, but you could always write a custom decorator to make your intent clear:

>>> from functools import lru_cache
>>> def only_call_once(fn):
...     return lru_cache(maxsize=None)(fn)
...
>>> @only_call_once
... def f():
...     print("Called!")
...     return 1
...
>>> f()
Called!
1
>>> f()
1

Paul



More information about the Python-list mailing list