|
| 1 | +.. |
| 2 | + Copyright 2009-2012 Ram Rachum. This work is licensed under a Creative |
| 3 | + Commons Attribution-ShareAlike 3.0 Unported License, with attribution to |
| 4 | + "Ram Rachum at ram.rachum.com" including link. The license may be obtained |
| 5 | + at http://creativecommons.org/licenses/by-sa/3.0/ |
| 6 | + |
| 7 | +.. _topics-caching-cache: |
| 8 | + |
| 9 | +:func:`caching.cache` |
| 10 | +==================== |
| 11 | + |
| 12 | +A caching decorator that understands arguments |
| 13 | +---------------------------------------------- |
| 14 | + |
| 15 | +The idea of a caching decorator is very cool. You decorate your function with a |
| 16 | +caching decorator: |
| 17 | + |
| 18 | + >>> from python_toolbox import caching |
| 19 | + >>> |
| 20 | + >>> @caching.cache |
| 21 | + ... def f(x): |
| 22 | + ... print('Calculating...') |
| 23 | + ... return x ** x # Some long expensive computation |
| 24 | + |
| 25 | +And then, every time you call it, it'll cache the results for next time: |
| 26 | + |
| 27 | + >>> f(4) |
| 28 | + Calculating... |
| 29 | + 256 |
| 30 | + >>> f(5) |
| 31 | + Calculating... |
| 32 | + 3125 |
| 33 | + >>> f(5) |
| 34 | + 3125 |
| 35 | + >>> f(5) |
| 36 | + 3125 |
| 37 | + |
| 38 | +As you can see, after the first time we calculate ``f(5)`` the result gets |
| 39 | +saved to a cache and every time we'll call ``f(5)`` Python will return the |
| 40 | +result from the cache instead of calculating it again. This prevents making |
| 41 | +redundant performance-expensive calculations. |
| 42 | + |
| 43 | +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:: |
| 44 | + |
| 45 | + def g(a, b=2, **kwargs): |
| 46 | + return whatever |
| 47 | + |
| 48 | +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. |
| 49 | + |
| 50 | +Enter :func:`caching.cache`: |
| 51 | + |
| 52 | + >>> @caching.cache() |
| 53 | + ... def g(a, b=2, **kwargs): |
| 54 | + ... print('Calculating') |
| 55 | + ... return (a, b, kwargs) |
| 56 | + ... |
| 57 | + >>> g(1) |
| 58 | + Calculating |
| 59 | + (1, 2, {}) |
| 60 | + >>> g(1, 2) # Look ma, no calculating: |
| 61 | + (1, 2, {}) |
| 62 | + >>> g(b=2, a=1) # No calculating again: |
| 63 | + (1, 2, {}) |
| 64 | + >>> g(1, 2, **{}) # No calculating here either: |
| 65 | + (1, 2, {}) |
| 66 | + >>> g('something_else') # Now calculating for different arguments: |
| 67 | + Calculating |
| 68 | + ('something_else', 2, {}) |
| 69 | + |
| 70 | +As you can see above, :func:`caching.cache` analyzes the function and understands |
| 71 | +that calls like ``g(1)`` and ``g(1, 2)`` are identical and therefore should be |
| 72 | +cached together. |
| 73 | + |
| 74 | + |
| 75 | +Both limited and unlimited cache |
| 76 | +-------------------------------- |
| 77 | + |
| 78 | +By default, the cache size will be unlimited. If you want to limit the cache size, pass in the ``max_size`` argument: |
| 79 | + |
| 80 | + >>> @caching.cache(max_size=7) |
| 81 | + ... def f(): pass |
| 82 | + |
| 83 | + |
| 84 | +If and when the cache size reaches the limit (7 in this case), old values will |
| 85 | +get thrown away according to a `LRU order`_. |
| 86 | + |
| 87 | + |
| 88 | +Sleekrefs |
| 89 | +---------- |
| 90 | + |
| 91 | +: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. |
| 92 | + |
| 93 | +The usage of sleekrefs prevents memory leaks when using potentially-heavy arguments. |
| 94 | + |
| 95 | + |
| 96 | +.. _LRU order: http://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used |
| 97 | +.. _weakrefs: http://docs.python.org/library/weakref.html |
0 commit comments