7

I want to have a function that can use functools.lru_cache, but not by default. I am looking for a way to use a function parameter that can be used to disable the lru_cache. Currently, I have a two versions of the function, one with lru_cache and one without. Then I have another function that wraps these with a parameter that can be used to control which function is used

def _no_cache(foo):
    print('_no_cache')
    return 1


@lru_cache()
def _with_cache(foo):
    print('_with_cache')
    return 0


def cache(foo, use_cache=False):
    if use_cache:
        return _with_cache(foo)
    return _no_cache(foo)

Is there a simpler way to do this?

TheStrangeQuark
  • 2,257
  • 5
  • 31
  • 58
  • 1
    You can't handle that from *inside* the function - if it's a cache hit, the function never even gets invoked. – jonrsharpe Jun 11 '19 at 13:01
  • 1
    all you can invent here properly - is just a Factory function which will return cached or uncached function object (depending on parameter), not the a function result – RomanPerekhrest Jun 11 '19 at 13:06
  • RomanPerekhest why would I want to return the function object instead of the function results? – TheStrangeQuark Jun 11 '19 at 13:14
  • @TheStrangeQuark, there are various scenarios of generating objects. No one compels you to extend a "home" code, it's up to you – RomanPerekhrest Jun 11 '19 at 13:46

2 Answers2

12

You can't disable the cache from inside the decorated function. However, you can simplify your code a bit by accessing the function directly via the __wrapped__ attribute.

From the documentation:

The original underlying function is accessible through the __wrapped__ attribute. This is useful for introspection, for bypassing the cache, or for rewrapping the function with a different cache.

Demo:

from functools import lru_cache

@lru_cache()
def f(arg):
    print(f"called with {arg}")
    return arg    

def call(arg, use_cache=False):
    if use_cache:
        return f(arg)
    return f.__wrapped__(arg)

call(1)
call(1, True)
call(2, True)
call(1, True)

Output:

called with 1
called with 1
called with 2
Eugene Yarmash
  • 142,882
  • 41
  • 325
  • 378
3

For inspection, you can use cache_info() method of wrapped function:

from functools import lru_cache

@lru_cache()
def my_function(foo):
    return foo * 2

def cache(foo, use_cache=False):
    if use_cache is False:
        return my_function.__wrapped__(foo)
    return my_function(foo)

print(cache(10, use_cache=True))    # cache miss
print(cache(10, use_cache=True))    # cache hit
print(cache(10, use_cache=False))   # bypass
print(cache(20, use_cache=False))   # bypass

print(my_function.cache_info())     # cache size=1, hit=1, miss=1

Prints:

20
20
20
40
CacheInfo(hits=1, misses=1, maxsize=128, currsize=1)
Andrej Kesely
  • 168,389
  • 15
  • 48
  • 91