.. Copyright 2009-2017 Ram Rachum. This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License, with attribution to "Ram Rachum at ram.rachum.com" including link. The license may be obtained at http://creativecommons.org/licenses/by-sa/3.0/ .. _topics-caching-cache: :func:`caching.cache` ==================== A caching decorator that understands arguments ---------------------------------------------- The idea of a caching decorator is very cool. You decorate your function with a caching decorator: >>> from python_toolbox import caching >>> >>> @caching.cache ... def f(x): ... print('Calculating...') ... return x ** x # Some long expensive computation And then, every time you call it, it'll cache the results for next time: >>> f(4) Calculating... 256 >>> f(5) Calculating... 3125 >>> f(5) 3125 >>> f(5) 3125 As you can see, after the first time we calculate ``f(5)`` the result gets saved to a cache and every time we'll call ``f(5)`` Python will return the result from the cache instead of calculating it again. This prevents making redundant performance-expensive calculations. Now, depending on the function, there can be many different ways to make the same call. For example, if you have a function defined like this:: def g(a, b=2, **kwargs): return whatever Then ``g(1)``, ``g(1, 2)``, ``g(b=2, a=1)`` and even ``g(1, 2, **{})`` are all equivalent. They give the exact same arguments, just in different ways. Most caching decorators out there don't understand that. If you call ``g(1)`` and then ``g(1, 2)``, they will calculate the function again, because they don't understand that it's exactly the same call and they could use the cached result. Enter :func:`caching.cache`: >>> @caching.cache() ... def g(a, b=2, **kwargs): ... print('Calculating') ... return (a, b, kwargs) ... >>> g(1) Calculating (1, 2, {}) >>> g(1, 2) # Look ma, no calculating: (1, 2, {}) >>> g(b=2, a=1) # No calculating again: (1, 2, {}) >>> g(1, 2, **{}) # No calculating here either: (1, 2, {}) >>> g('something_else') # Now calculating for different arguments: Calculating ('something_else', 2, {}) As you can see above, :func:`caching.cache` analyzes the function and understands that calls like ``g(1)`` and ``g(1, 2)`` are identical and therefore should be cached together. Both limited and unlimited cache -------------------------------- By default, the cache size will be unlimited. If you want to limit the cache size, pass in the ``max_size`` argument: >>> @caching.cache(max_size=7) ... def f(): pass If and when the cache size reaches the limit (7 in this case), old values will get thrown away according to a `LRU order`_. Sleekrefs ---------- :func:`caching.cache` arguments with sleekrefs. Sleekrefs are a more robust variation of `weakrefs`_. They are basically a gracefully-degrading version of weakrefs, so you can use them on un-weakreff-able objects like :class:`int`\, and they will just use regular references. The usage of sleekrefs prevents memory leaks when using potentially-heavy arguments. .. _LRU order: http://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used .. _weakrefs: http://docs.python.org/library/weakref.html