Update HISTORY.rst
[cached-property.git] / README.rst
1 ===============================
2 cached-property
3 ===============================
4
5 .. image:: https://badge.fury.io/py/cached-property.png
6 :target: http://badge.fury.io/py/cached-property
7
8 .. image:: https://travis-ci.org/pydanny/cached-property.png?branch=master
9 :target: https://travis-ci.org/pydanny/cached-property
10
11 .. image:: https://pypip.in/d/cached-property/badge.png
12 :target: https://pypi.python.org/pypi/cached-property
13
14
15 A cached-property for decorating methods in classes.
16
17 Why?
18 -----
19
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.
23
24 How to use it
25 --------------
26
27 Let's define a class with an expensive property. Every time you stay there the
28 price goes up by $50!
29
30 .. code-block:: python
31
32 class Monopoly(object):
33
34 def __init__(self):
35 self.boardwalk_price = 500
36
37 @property
38 def boardwalk(self):
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
43
44 Now run it:
45
46 .. code-block:: python
47
48 >>> monopoly = Monopoly()
49 >>> monopoly.boardwalk
50 550
51 >>> monopoly.boardwalk
52 600
53
54 Let's convert the boardwalk property into a `cached_property`.
55
56
57 .. code-block:: python
58
59 from cached_property import cached_property
60
61 class Monopoly(object):
62
63 def __init__(self):
64 self.boardwalk_price = 500
65
66 @cached_property
67 def boardwalk(self):
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
72
73 Now when we run it the price stays at $550.
74
75 .. code-block:: python
76
77 >>> monopoly = Monopoly()
78 >>> monopoly.boardwalk
79 550
80 >>> monopoly.boardwalk
81 550
82 >>> monopoly.boardwalk
83 550
84
85 Why doesn't the value of `monopoly.boardwalk` change? Because it's a **cached property**!
86
87 Invalidating the Cache
88 ----------------------
89
90 Results of cached functions can be invalidated by outside forces. Let's demonstrate how to force the cache to invalidate:
91
92 .. code-block:: python
93
94 >>> monopoly = Monopoly()
95 >>> monopoly.boardwalk
96 550
97 >>> monopoly.boardwalk
98 550
99 >>> # invalidate the cache
100 >>> del monopoly.boardwalk
101 >>> # request the boardwalk property again
102 >>> monopoly.boardwalk
103 600
104 >>> monopoly.boardwalk
105 600
106
107 Working with Threads
108 ---------------------
109
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`:
111
112 .. code-block:: python
113
114 import threading
115
116 from cached_property import threaded_cached_property
117
118 class Monopoly(object):
119
120 def __init__(self):
121 self.boardwalk_price = 500
122 self.lock = threading.Lock()
123
124 @threaded_cached_property
125 def boardwalk(self):
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."""
129
130 sleep(1)
131 # Need to guard this since += isn't atomic.
132 with self.lock:
133 self.boardwalk_price += 50
134 return self.boardwalk_price
135
136 Now use it:
137
138 .. code-block:: python
139
140 >>> from threading import Thread
141 >>> from monopoly import Monopoly
142 >>> monopoly = Monopoly()
143 >>> threads = []
144 >>> for x in range(10):
145 >>> thread = Thread(target=lambda: monopoly.boardwalk)
146 >>> thread.start()
147 >>> threads.append(thread)
148
149 >>> for thread in threads:
150 >>> thread.join()
151
152 >>> self.assertEqual(m.boardwalk, 550)
153
154
155 Credits
156 --------
157
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.
161
162 .. _`@audreyr`: https://github.com/audreyr
163 .. _`cookiecutter`: https://github.com/audreyr/cookiecutter