I am trying to implement a cache which can be persisted. Also, some of the functions that I am trying to cache takes arguments that are not python objects (external library objects). But they seem to have a hash
value. My first try was to use lru_cache
but it didn't have an option to persist across sessions. Then I stumbled upon this answer which explained a way to create a custom decorator to cache the function results. I made some minor changes to it and came up with this decorator (which can take arguments) now.
def cached(maxsize=None, hashkey=True, persist=False):
"""Decorator method to cache an expensive function
Args:
maxsize (int): The maximum number of cached results to store
if None, the size is indefinite.
hashkey (bool): If the arguments of the function are objects
which cannot be pickled, use the hashkey option which will
hash the arguments.
persist (bool): Write the cached results to the disk so that
it can be read in even after the session has ended.
Returns:
Decorator function
"""
def outer(func):
func.cache = {}
func.cache_file = f"{func.__name__}.cache"
@wraps(func)
def wrapper(*args):
if hashkey:
key = hash(args)
else:
key = args
try:
return func.cache[key]
except KeyError:
try:
if persist:
with open(func.cache_file, "rb") as cachefile:
func.cache = pickle.load(cachefile)
return func.cache[key]
except (KeyError, FileNotFound):
func.cache[key] = result = func(*args)
if maxsize and len(func.cache) > maxsize:
func.cache.pop(next(iter(func.cache))) # removes the first item
if persist:
with open(func.cache_file, "wb") as cachefile:
pickle.dump(func.cache, cachefile)
return result
return wrapper
return outer
The functions that I am planning to cache may be called at most 10000 times with a given cache file.
Here are my questions:
- Is there anything obviously wrong with this code?
- Is hashing the arguments a bad idea?
- Are there any obvious places where I can store the cache file?