1 ===============================
3 ===============================
5 .. image:: https://badge.fury.io/py/cached-property.png
6 :target: http://badge.fury.io/py/cached-property
8 .. image:: https://travis-ci.org/pydanny/cached-property.png?branch=master
9 :target: https://travis-ci.org/pydanny/cached-property
11 .. image:: https://pypip.in/d/cached-property/badge.png
12 :target: https://pypi.python.org/pypi/cached-property
15 A cached-property for decorating methods in classes.
20 * Makes caching of time or computational expensive properties quick and easy.
21 * Because I got tired of copy/pasting this code from non-web project to non-web project.
22 * I needed something really simple that worked in Python 2 and 3.
27 Let's define a class with an expensive property. Every time you stay there the
30 .. code-block:: python
32 class Monopoly(object):
35 self.boardwalk_price = 500
39 # In reality, this might represent a database call or time
40 # intensive task like calling a third-party API.
41 self.boardwalk_price += 50
42 return self.boardwalk_price
46 .. code-block:: python
48 >>> monopoly = Monopoly()
49 >>> monopoly.boardwalk
51 >>> monopoly.boardwalk
54 Let's convert the boardwalk property into a `cached_property`.
57 .. code-block:: python
59 from cached_property import cached_property
61 class Monopoly(object):
64 self.boardwalk_price = 500
68 # Again, this is a silly example. Don't worry about it, this is
69 # just an example for clarity.
70 self.boardwalk_price += 50
71 return self.boardwalk_price
73 Now when we run it the price stays at $550.
75 .. code-block:: python
77 >>> monopoly = Monopoly()
78 >>> monopoly.boardwalk
80 >>> monopoly.boardwalk
82 >>> monopoly.boardwalk
85 Why doesn't the value of `monopoly.boardwalk` change? Because it's a **cached property**!
87 Invalidating the Cache
88 ----------------------
90 Results of cached functions can be invalidated by outside forces. Let's demonstrate how to force the cache to invalidate:
92 .. code-block:: python
94 >>> monopoly = Monopoly()
95 >>> monopoly.boardwalk
97 >>> monopoly.boardwalk
99 >>> # invalidate the cache
100 >>> del monopoly.boardwalk
101 >>> # request the boardwalk property again
102 >>> monopoly.boardwalk
104 >>> monopoly.boardwalk
108 ---------------------
110 What if a whole bunch of people want to stay at Boardwalk all at once? This means using threads, which unfortunately causes problems with the standard `cached_property`. In this case, switch to using the `threaded_cached_property`:
112 .. code-block:: python
116 from cached_property import threaded_cached_property
118 class Monopoly(object):
121 self.boardwalk_price = 500
122 self.lock = threading.Lock()
124 @threaded_cached_property
126 """threaded_cached_property is really nice for when no one waits
127 for other people to finish their turn and rudely start rolling
128 dice and moving their pieces."""
131 # Need to guard this since += isn't atomic.
133 self.boardwalk_price += 50
134 return self.boardwalk_price
138 .. code-block:: python
140 >>> from threading import Thread
141 >>> from monopoly import Monopoly
142 >>> monopoly = Monopoly()
144 >>> for x in range(10):
145 >>> thread = Thread(target=lambda: monopoly.boardwalk)
147 >>> threads.append(thread)
149 >>> for thread in threads:
152 >>> self.assertEqual(m.boardwalk, 550)
158 * Pip, Django, Werkzueg, Bottle, Pyramid, and Zope for having their own implementations. This package uses an implementation that matches the Bottle version.
159 * Reinout Van Rees for pointing out the `cached_property` decorator to me.
160 * My awesome wife `@audreyr`_ who created `cookiecutter`_, which meant rolling this out took me just 15 minutes.
162 .. _`@audreyr`: https://github.com/audreyr
163 .. _`cookiecutter`: https://github.com/audreyr/cookiecutter