# Is there a decorator to simply cache function return values?

Each Answer to this Q is separated by one/two green lines.

Consider the following:

``````@property
def name(self):

if not hasattr(self, '_name'):

# expensive calculation
self._name = 1 + 1

return self._name
``````

I’m new, but I think the caching could be factored out into a decorator. Only I didn’t find one like it ðŸ˜‰

PS the real calculation doesn’t depend on mutable values

Starting from Python 3.2 there is a built-in decorator:

`@functools.lru_cache(maxsize=100, typed=False)`

Decorator to wrap a function with a memoizing callable that saves up to the maxsize most recent calls. It can save time when an expensive or I/O bound function is periodically called with the same arguments.

Example of an LRU cache for computing Fibonacci numbers:

``````from functools import lru_cache

@lru_cache(maxsize=None)
def fib(n):
if n < 2:
return n
return fib(n-1) + fib(n-2)

>>> print([fib(n) for n in range(16)])
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]

>>> print(fib.cache_info())
CacheInfo(hits=28, misses=16, maxsize=None, currsize=16)
``````

If you are stuck with Python 2.x, here’s a list of other compatible memoization libraries:

It sounds like you’re not asking for a general-purpose memoization decorator (i.e., you’re not interested in the general case where you want to cache return values for different argument values). That is, you’d like to have this:

``````x = obj.name  # expensive
y = obj.name  # cheap
``````

while a general-purpose memoization decorator would give you this:

``````x = obj.name()  # expensive
y = obj.name()  # cheap
``````

I submit that the method-call syntax is better style, because it suggests the possibility of expensive computation while the property syntax suggests a quick lookup.

[Update: The class-based memoization decorator I had linked to and quoted here previously doesn’t work for methods. I’ve replaced it with a decorator function.] If you’re willing to use a general-purpose memoization decorator, here’s a simple one:

``````def memoize(function):
memo = {}
def wrapper(*args):
if args in memo:
return memo[args]
else:
rv = function(*args)
memo[args] = rv
return rv
return wrapper
``````

Example usage:

``````@memoize
def fibonacci(n):
if n < 2: return n
return fibonacci(n - 1) + fibonacci(n - 2)
``````

Another memoization decorator with a limit on the cache size can be found here.

Python 3.8 `functools.cached_property` decorator

https://docs.python.org/dev/library/functools.html#functools.cached_property

`cached_property` from Werkzeug was mentioned at: https://stackoverflow.com/a/5295190/895245 but a supposedly derived version will be merged into 3.8, which is awesome.

This decorator can be seen as caching `@property`, or as a cleaner `@functools.lru_cache` for when you don’t have any arguments.

The docs say:

``````@functools.cached_property(func)
``````

Transform a method of a class into a property whose value is computed once and then cached as a normal attribute for the life of the instance. Similar to property(), with the addition of caching. Useful for expensive computed properties of instances that are otherwise effectively immutable.

Example:

``````class DataSet:
def __init__(self, sequence_of_numbers):
self._data = sequence_of_numbers

@cached_property
def stdev(self):
return statistics.stdev(self._data)

@cached_property
def variance(self):
return statistics.variance(self._data)
``````

New in version 3.8.

Note This decorator requires that the dict attribute on each instance be a mutable mapping. This means it will not work with some types, such as metaclasses (since the dict attributes on type instances are read-only proxies for the class namespace), and those that specify slots without including dict as one of the defined slots (as such classes donâ€™t provide a dict attribute at all).

``````class memorize(dict):
def __init__(self, func):
self.func = func

def __call__(self, *args):
return self[args]

def __missing__(self, key):
result = self[key] = self.func(*key)
return result
``````

Sample uses:

``````>>> @memorize
... def foo(a, b):
...     return a * b
>>> foo(2, 4)
8
>>> foo
{(2, 4): 8}
>>> foo('hi', 3)
'hihihi'
>>> foo
{(2, 4): 8, ('hi', 3): 'hihihi'}
``````

`functools.cache` has been released in Python 3.9 (docs):

``````from functools import cache

@cache
def factorial(n):
return n * factorial(n-1) if n else 1
``````

In previous Python versions, one of the early answers is still a valid solution using `lru_cache` as an ordinary cache without the limit and lru feature. (docs)

If maxsize is set to None, the LRU feature is disabled and the cache
can grow without bound.

Here is a prettier version of it:

``````cache = lru_cache(maxsize=None)

@cache
def func(param1):
pass
``````

Werkzeug has a `cached_property` decorator (docs, source)

I coded this simple decorator class to cache function responses. I find it VERY useful for my projects:

``````from datetime import datetime, timedelta

class cached(object):
def __init__(self, *args, **kwargs):
self.cached_function_responses = {}
self.default_max_age = kwargs.get("default_cache_max_age", timedelta(seconds=0))

def __call__(self, func):
def inner(*args, **kwargs):
max_age = kwargs.get('max_age', self.default_max_age)
if not max_age or func not in self.cached_function_responses or (datetime.now() - self.cached_function_responses[func]['fetch_time'] > max_age):
if 'max_age' in kwargs: del kwargs['max_age']
res = func(*args, **kwargs)
self.cached_function_responses[func] = {'data': res, 'fetch_time': datetime.now()}
return self.cached_function_responses[func]['data']
return inner
``````

The usage is straightforward:

``````import time

@cached
def myfunc(a):
print "in func"
return (a, datetime.now())

@cached(default_max_age = timedelta(seconds=6))
def cacheable_test(a):
print "in cacheable test: "
return (a, datetime.now())

print cacheable_test(1,max_age=timedelta(seconds=5))
print cacheable_test(2,max_age=timedelta(seconds=5))
time.sleep(7)
print cacheable_test(3,max_age=timedelta(seconds=5))
``````

DISCLAIMER: I’m the author of kids.cache.

You should check `kids.cache`, it provides a `@cache` decorator that works on python 2 and python 3. No dependencies, ~100 lines of code. It’s very straightforward to use, for instance, with your code in mind, you could use it like this:

``````pip install kids.cache
``````

Then

``````from kids.cache import cache
...
class MyClass(object):
...
@cache            # <-- That's all you need to do
@property
def name(self):
return 1 + 1  # supposedly expensive calculation
``````

Or you could put the `@cache` decorator after the `@property` (same result).

Using cache on a property is called lazy evaluation, `kids.cache` can do much more (it works on function with any arguments, properties, any type of methods, and even classes…). For advanced users, `kids.cache` supports `cachetools` which provides fancy cache stores to python 2 and python 3 (LRU, LFU, TTL, RR cache).

IMPORTANT NOTE: the default cache store of `kids.cache` is a standard dict, which is not recommended for long running program with ever different queries as it would lead to an ever growing caching store. For this usage you can plugin other cache stores using for instance (`@cache(use=cachetools.LRUCache(maxsize=2))` to decorate your function/property/class/method…)

Ah, just needed to find the right name for this: “Lazy property evaluation“.

I do this a lot too; maybe I’ll use that recipe in my code sometime.

There is yet another example of a memoize decorator at Python Wiki:

http://wiki.python.org/moin/PythonDecoratorLibrary#Memoize

That example is a bit smart, because it won’t cache the results if the parameters are mutable. (check that code, it’s very simple and interesting!)

If you are using Django Framework, it has such a property to cache a view or response of API’s
using `@cache_page(time)` and there can be other options as well.

Example:

``````@cache_page(60 * 15, cache="special_cache")
def my_view(request):
...
``````

More details can be found here.

There is fastcache, which is “C implementation of Python 3 functools.lru_cache. Provides speedup of 10-30x over standard library.”

Same as chosen answer, just different import:

``````from fastcache import lru_cache
@lru_cache(maxsize=128, typed=False)
def f(a, b):
pass
``````

Also, it comes installed in Anaconda, unlike functools which needs to be installed.

``````from joblib import Memory
memory = Memory(cachedir=cachedir, verbose=0)
@memory.cache
def f(x):
print('Running f(%s)' % x)
return x
``````

Along with the Memoize Example I found the following python packages:

• cachepy; It allows to set up ttl and\or the number of calls for cached functions; Also, one can use encrypted file-based cache…
• percache

`@lru_cache` is not good with default attrs

my `@mem` decorator:

``````import inspect
from copy import deepcopy
from functools import lru_cache, wraps
from typing import Any, Callable, Dict, Iterable

# helper
def get_all_kwargs_values(f: Callable, kwargs: Dict[str, Any]) -> Iterable[Any]:
default_kwargs = {
k: v.default
for k, v in inspect.signature(f).parameters.items()
if v.default is not inspect.Parameter.empty
}

all_kwargs = deepcopy(default_kwargs)
all_kwargs.update(kwargs)

for key in sorted(all_kwargs.keys()):
yield all_kwargs[key]

# the best decorator
def mem(func: Callable) -> Callable:
cache = dict()

@wraps(func)
def wrapper(*args, **kwargs) -> Any:
all_kwargs_values = get_all_kwargs_values(func, kwargs)
params = (*args, *all_kwargs_values)
_hash = hash(params)

if _hash not in cache:
cache[_hash] = func(*args, **kwargs)

return cache[_hash]

return wrapper

# some logic
def counter(*args) -> int:
print(f'* not_cached:', end='\t')
return sum(args)

@mem
def check_mem(a, *args, z=10) -> int:
return counter(a, *args, z)

@lru_cache
def check_lru(a, *args, z=10) -> int:
return counter(a, *args, z)

def test(func) -> None:
print(f'\nTest {func.__name__}:')

print('*', func(1, 2, 3, 4, 5))
print('*', func(1, 2, 3, 4, 5))
print('*', func(1, 2, 3, 4, 5, z=6))
print('*', func(1, 2, 3, 4, 5, z=6))
print('*', func(1))
print('*', func(1, z=10))

def main():
test(check_mem)
test(check_lru)

if __name__ == '__main__':
main()
``````

output:

``````Test check_mem:
* not_cached:   * 25
* 25
* not_cached:   * 21
* 21
* not_cached:   * 11
* 11

Test check_lru:
* not_cached:   * 25
* 25
* not_cached:   * 21
* 21
* not_cached:   * 11
* not_cached:   * 11
``````

I implemented something like this, using pickle for persistance and using sha1 for short almost-certainly-unique IDs. Basically the cache hashed the code of the function and the hist of arguments to get a sha1 then looked for a file with that sha1 in the name. If it existed, it opened it and returned the result; if not, it calls the function and saves the result (optionally only saving if it took a certain amount of time to process).

That said, I’d swear I found an existing module that did this and find myself here trying to find that module… The closest I can find is this, which looks about right: http://chase-seibert.github.io/blog/2011/11/23/pythondjango-disk-based-caching-decorator.html

The only problem I see with that is it wouldn’t work well for large inputs since it hashes str(arg), which isn’t unique for giant arrays.

It would be nice if there were a unique_hash() protocol that had a class return a secure hash of its contents. I basically manually implemented that for the types I cared about.

If you are using Django and want to cache views, see Nikhil Kumar’s answer.

But if you want to cache ANY function results, you can use django-cache-utils.

It reuses Django caches and provides easy to use `cached` decorator:

``````from cache_utils.decorators import cached

@cached(60)
def foo(x, y=0):
print 'foo is called'
return x+y
``````

# Function cache simple solution

## with ttl (time to life) and max_entries

• doesnt work when the decorated function takes unhashable types as input (e.g. dicts)
• optional parameter: ttl (time to live for every entry)
• optional parameter: max_entries (if too many cache argument combination to no clutter the storage)
• make sure the function has no important side effects

# Example use

``````import time

@cache(ttl=timedelta(minutes=3), max_entries=300)
time.sleep(2)
return a + b

@cache()
def substract(a, b):
time.sleep(2)
return a - b

a = 5
# function is called with argument combinations the first time -> it takes some time
for i in range(5):

# function is called with same arguments again? -> will answer from cache
for i in range(5):
``````

# Copy the decorator code

``````from datetime import datetime, timedelta

def cache(**kwargs):
def decorator(function):
# static function variable for cache, lazy initialization
try: function.cache
except: function.cache = {}
def wrapper(*args):
# if nothing valid in cache, insert something
if not args in function.cache or datetime.now() > function.cache[args]['expiry']:
if 'max_entries' in kwargs:
max_entries = kwargs['max_entries']
if max_entries != None and len(function.cache) >= max_entries:
now = datetime.now()
# delete the the first expired entry that can be found (lazy deletion)
for key in function.cache:
if function.cache[key]['expiry'] < now:
del function.cache[key]
break
# if nothing is expired that is deletable, delete the first
if len(function.cache) >= max_entries:
del function.cache[next(iter(function.cache))]
function.cache[args] = {'result': function(*args), 'expiry': datetime.max if 'ttl' not in kwargs else datetime.now() + kwargs['ttl']}

return function.cache[args]['result']
return wrapper
return decorator
``````

``````from functools import wraps

def cache(maxsize=128):
cache = {}

def decorator(func):
@wraps(func)
def inner(*args, no_cache=False, **kwargs):
if no_cache:
return func(*args, **kwargs)

key_base = "_".join(str(x) for x in args)
key_end = "_".join(f"{k}:{v}" for k, v in kwargs.items())
key = f"{key_base}-{key_end}"

if key in cache:
return cache[key]

res = func(*args, **kwargs)

if len(cache) > maxsize:
del cache[list(cache.keys())[0]]
cache[key] = res

return res

return inner

return decorator

def async_cache(maxsize=128):
cache = {}

def decorator(func):
@wraps(func)
async def inner(*args, no_cache=False, **kwargs):
if no_cache:
return await func(*args, **kwargs)

key_base = "_".join(str(x) for x in args)
key_end = "_".join(f"{k}:{v}" for k, v in kwargs.items())
key = f"{key_base}-{key_end}"

if key in cache:
return cache[key]

res = await func(*args, **kwargs)

if len(cache) > maxsize:
del cache[list(cache.keys())[0]]
cache[key] = res

return res

return inner

return decorator
``````

## Example use

``````import asyncio
import aiohttp

# Removes the aiohttp ClientSession instance warning.
class HTTPSession(aiohttp.ClientSession):
""" Abstract class for aiohttp. """

def __init__(self, loop=None) -> None:
super().__init__(loop=loop or asyncio.get_event_loop())

def __del__(self) -> None:
if not self.closed:
self.loop.run_until_complete(self.close())
self.loop.close()

return

session = HTTPSession()

@async_cache()
async def query(url, method="get", res_method="text", *args, **kwargs):
async with getattr(session, method.lower())(url, *args, **kwargs) as res:
return await getattr(res, res_method)()

async def get(url, *args, **kwargs):
return await query(url, "get", *args, **kwargs)

async def post(url, *args, **kwargs):
return await query(url, "post", *args, **kwargs)

async def delete(url, *args, **kwargs):
return await query(url, "delete", *args, **kwargs)
``````

The answers/resolutions are collected from stackoverflow, are licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0 .