3

What's the best way to cache a function in python with an optional caching parameter?
I've a function which performs a database request and does not take any arguments. Most of the times it's okay if the function uses the cached result but sometimes I want to send a new database request.

from functools import lru_cache

@lru_cache
def my_database_request_function(use_cache: bool = False):

    if use_cache:
    # takes response from cache
    else:
    # makes database request

    return response



The implementation above won't work because the second time the functions gets called with the use_cache=False parameter it will not make a new request.

j. DOE
  • 238
  • 1
  • 2
  • 15
  • 1
    Well, just remove `lru_cache` decorator and implement caching yourself. – Olvin Roght Feb 10 '22 at 16:22
  • Related: https://stackoverflow.com/questions/37653784/how-do-i-use-cache-clear-on-python-functools-lru-cache && https://stackoverflow.com/questions/40273767/clear-all-lru-cache-in-python - maybe good dupe targets as well – Patrick Artner Feb 10 '22 at 16:48

3 Answers3

3

Use lru_cache's cache_clear() to clear the cached value and force a new retrieval:

[...] The decorator also provides a cache_clear() function for clearing or invalidating the cache. [...]

Demo:

from functools import lru_cache

what = 1     # global value for demo purposes

@lru_cache
def my_database_request_function():
    return what

print(my_database_request_function())

what = 42                                  # change value

print(my_database_request_function())      # gets cached value

my_database_request_function.cache_clear() # clear cache

print(my_database_request_function())      # not cached

Output:

1
1  # after changing the global 'what' variable, still gets cached result
42 # after .cache_clear()
Patrick Artner
  • 50,409
  • 9
  • 43
  • 69
1
from multipledispatch import dispatch
from functools import lru_cache

@dispatch(bool)
@lru_cache
def my_database_request_function(use_cache: bool):
    print('cached')

@dispatch()
def my_database_request_function():
    print('NOT cached')

my_database_request_function(True)
my_database_request_function(False)
my_database_request_function()

Output:

cached
cached
NOT cached

Using a module called multipledispatch, you can do function overloading where there are multiple functions of the same name and you specify the different parameters for each one. The bool here can change to anything you prefer.

Tario You
  • 347
  • 2
  • 7
  • So `my_database_request_function(use_cache=False)` uses the cached version? That doesn't seem to be correct. – a_guest Feb 10 '22 at 16:35
  • @a_guest two new functions are created: one with no arguments, and one with a bool argument so when you call "my_database_request_function(use_cache=False)", it runs the function that has the bool argument. This is only for demonstration, the point is to change that argument to something that would make sense yet at the same time be different from the uncached argument. – Tario You Feb 11 '22 at 11:20
0

Here's a straightforward approach with helper functions:

from functools import lru_cache

def my_database_request_function(use_cache: bool = False):
    if use_cache:
        return _my_database_request_function_cached()
    else:
        return _my_database_request_function_fresh()

@lru_cache
def _my_database_request_function_cached():
    return _my_database_request_function_fresh()

def _my_database_request_function_fresh():
    # do actual database request
Samwise
  • 68,105
  • 3
  • 30
  • 44
  • calling _cached()_ then _fresh()_ then _cached()_ again will give you different results if changed - _cached()_ will alwayrs cache the 1st result? – Patrick Artner Feb 10 '22 at 16:31