@lru_cache can be used elegantly to create a cache with a time-to-live, if a time parameter is used to invalidate previous responses: from functools import lru_cache import time @lru_cache() def my_expensive_function(a, b, ttl_hash=None): del ttl_hash # to emphasize we don't use it and to shut pylint up return a + b def get_ttl_hash(seconds=3600): """Return the same value within `seconds` time period""" return round(time.time() / seconds) # somewhere in your code... res = my_expensive_function(2, 2, ttl_hash=get_ttl_hash()) # cache will be updated once in an hour (Source) A more elegant version that does not require external parameters (source): from functools import lru_cache, wraps from datetime import datetime, timedelta def timed_lru_cache(seconds: int, maxsize: int = 128): def wrapper_cache(func): func = lru_cache(maxsize=maxsize)(func) func.lifetime = timedelta(seconds=seconds) func.expiration = datetime.utcnow() + func.lifetime @wraps(func) def wrapped_func(*args, **kwargs): if datetime.utcnow() >= func.expiration: func.cache_clear() func.expiration = datetime.utcnow() + func.lifetime return func(*args, **kwargs) return wrapped_func return wrapper_cache