forked from yasoob/intermediatePython
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdecorators.html
More file actions
336 lines (287 loc) · 22.5 KB
/
Copy pathdecorators.html
File metadata and controls
336 lines (287 loc) · 22.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Decorators</title>
<link rel="stylesheet" href="_static/epub.css" type="text/css" />
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
</head>
<body role="document">
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="global_&_return.html" title="Global & Return"
accesskey="N">next</a></li>
<li class="right" >
<a href="ternary_operators.html" title="Ternary Operators"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="index.html">Python Tips 0.1 documentation</a> »</li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="body" role="main">
<div class="section" id="decorators">
<h1>Decorators</h1>
<p>Decorators are a significant part of Python. In simple words they are
functions which modify the functionality of another function. They help
to make our code shorter and more Pythonic. Most of the beginners do not
know where to use them so I am going to share some areas where
decorators can make your code consise.</p>
<p>Firstly let’s discuss how to write your own decorator.</p>
<p>It is perhaps one of the most difficult concept to grasp. We will take
it one step at a time so that you can fully inderstand it.</p>
<div class="section" id="everything-in-python-is-an-object">
<h2>Everything in python is an object:</h2>
<p>First of all let’s understand functions in python:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">hi</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s">"yasoob"</span><span class="p">):</span>
<span class="k">return</span> <span class="s">"hi "</span><span class="o">+</span><span class="n">name</span>
<span class="k">print</span> <span class="n">hi</span><span class="p">()</span>
<span class="c">#output: 'hi yasoob'</span>
<span class="c">#We can even assign a function to a variable like</span>
<span class="n">greet</span> <span class="o">=</span> <span class="n">hi</span>
<span class="c">#We are not using parentheses here because we are not calling the function hi</span>
<span class="c">#instead we are just putting it into the greet variable. Let's try to run this</span>
<span class="k">print</span> <span class="n">greet</span><span class="p">()</span>
<span class="c">#output: 'hi yasoob'</span>
<span class="c">#lets see what happens if we delete the old hi function!</span>
<span class="k">del</span> <span class="n">hi</span>
<span class="k">print</span> <span class="n">hi</span><span class="p">()</span>
<span class="c">#outputs: NameError</span>
<span class="k">print</span> <span class="n">greet</span><span class="p">()</span>
<span class="c">#outputs: 'hi yasoob'</span>
</pre></div>
</div>
</div>
<div class="section" id="defining-functions-within-functions">
<h2>Defining functions within functions:</h2>
<p>So those are the basics when it comes to functions. Lets take your
knowledge one step further. In Python we can define functions inside
other functions:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">hi</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s">"yasoob"</span><span class="p">):</span>
<span class="k">print</span> <span class="s">"now you are inside the hi() function"</span>
<span class="k">def</span> <span class="nf">greet</span><span class="p">():</span>
<span class="k">return</span> <span class="s">"now you are in the greet() function"</span>
<span class="k">def</span> <span class="nf">welcome</span><span class="p">():</span>
<span class="k">return</span> <span class="s">"now you are in the welcome() function"</span>
<span class="k">print</span> <span class="n">greet</span><span class="p">()</span>
<span class="k">print</span> <span class="n">welcome</span><span class="p">()</span>
<span class="k">print</span> <span class="s">"now you are back in the hi() function"</span>
<span class="n">hi</span><span class="p">()</span>
<span class="c">#output:now you are inside the hi() function</span>
<span class="c"># now you are in the greet() function</span>
<span class="c"># now you are in the welcome() function</span>
<span class="c"># now you are back in the hi() function</span>
<span class="c"># This shows that whenever you call hi(), greet() and welcome()</span>
<span class="c"># are also called. However the greet() and welcome() functions</span>
<span class="c"># are not available outsite the hi() function e.g:</span>
<span class="n">greet</span><span class="p">()</span>
<span class="c">#outputs: NameError: name 'greet' is not defined</span>
</pre></div>
</div>
<p>So now we know that we can define functions in other functions. In
simpler words we can make nested functions. Now you need to learn one
more thing that functions can return functions too.</p>
</div>
<div class="section" id="returning-functions-from-within-functions">
<h2>Returning functions from within functions:</h2>
<p>It is not necessary to execute a function within another function, we
can return it as an output as well:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">hi</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s">"yasoob"</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">greet</span><span class="p">():</span>
<span class="k">return</span> <span class="s">"now you are in the greet() function"</span>
<span class="k">def</span> <span class="nf">welcome</span><span class="p">():</span>
<span class="k">return</span> <span class="s">"now you are in the welcome() function"</span>
<span class="k">if</span> <span class="n">name</span> <span class="o">==</span> <span class="s">"yasoob"</span><span class="p">:</span>
<span class="k">return</span> <span class="n">greet</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="n">welcome</span>
<span class="n">a</span> <span class="o">=</span> <span class="n">hi</span><span class="p">()</span>
<span class="k">print</span> <span class="n">a</span>
<span class="c">#outputs: <function greet at 0x7f2143c01500></span>
<span class="c">#This clearly shows that `a` now points to the greet() function in hi()</span>
<span class="c">#Now try this</span>
<span class="k">print</span> <span class="n">a</span><span class="p">()</span>
<span class="c">#outputs: now you are in the greet() function</span>
</pre></div>
</div>
<p>Just take a look at the code again. In the <code class="docutils literal"><span class="pre">if/else</span></code> clause we are
returning <code class="docutils literal"><span class="pre">greet</span></code> and <code class="docutils literal"><span class="pre">welcome</span></code>, not <code class="docutils literal"><span class="pre">greet()</span></code> and <code class="docutils literal"><span class="pre">welcome()</span></code>.
Why is that? It is so because when you put parentheses around it the
function gets executed whereas if you don’t put parenthesis around it
then it can be passed around and can be assigned to other variables
without executing it. Did you get it ? Let me explain it a little bit in
more detail. When we write <code class="docutils literal"><span class="pre">a</span> <span class="pre">=</span> <span class="pre">hi()</span></code>, <code class="docutils literal"><span class="pre">hi()</span></code> gets executed and
because the name is yasoob by default, the function greet is returned.
If we change the statement to <code class="docutils literal"><span class="pre">a</span> <span class="pre">=</span> <span class="pre">hi(name</span> <span class="pre">=</span> <span class="pre">"ali")</span></code> then the welcome
function will be returned. We can also do print <code class="docutils literal"><span class="pre">hi()()</span></code> which outputs
<em>now you are in the greet() function</em>.</p>
</div>
<div class="section" id="giving-a-function-as-an-argument-to-another-function">
<h2>Giving a function as an argument to another function:</h2>
<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">hi</span><span class="p">():</span>
<span class="k">return</span> <span class="s">"hi yasoob!"</span>
<span class="k">def</span> <span class="nf">doSomethingBeforeHi</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
<span class="k">print</span> <span class="s">"I am doing some boring work before executing hi()"</span>
<span class="k">print</span> <span class="n">func</span><span class="p">()</span>
<span class="n">doSomethingBeforeHi</span><span class="p">(</span><span class="n">hi</span><span class="p">)</span>
<span class="c">#outputs:I am doing some boring work before executing hi()</span>
<span class="c"># hi yasoob!</span>
</pre></div>
</div>
<p>Now you have all the required knowledge to learn what decorators really
are. Decorators let you execute code before and after a function.</p>
</div>
<div class="section" id="writing-your-first-decorator">
<h2>Writing your first decorator:</h2>
<p>In the last example we actually made a decorator! Lets modify the
previous decorator and make a little bit more usable program:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">a_new_decorator</span><span class="p">(</span><span class="n">a_func</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">wrapTheFunction</span><span class="p">():</span>
<span class="k">print</span> <span class="s">"I am doing some boring work before executing a_func()"</span>
<span class="n">a_func</span><span class="p">()</span>
<span class="k">print</span> <span class="s">"I am doing some boring work after executing a_func()"</span>
<span class="k">return</span> <span class="n">wrapTheFunction</span>
<span class="k">def</span> <span class="nf">a_function_requiring_decoration</span><span class="p">():</span>
<span class="k">print</span> <span class="s">"I am the function which needs some decoration to remove my foul smell"</span>
<span class="n">a_function_requiring_decoration</span><span class="p">()</span>
<span class="c">#outputs: "I am the function which needs some decoration to remove my foul smell"</span>
<span class="n">a_function_requiring_decoration</span> <span class="o">=</span> <span class="n">a_new_decorator</span><span class="p">(</span><span class="n">a_function_requiring_decoration</span><span class="p">)</span>
<span class="c">#now a_function_requiring_decoration is wrapped by wrapTheFunction()</span>
<span class="n">a_function_requiring_decoration</span><span class="p">()</span>
<span class="c">#outputs:I am doing some boring work before executing a_function_requiring_decoration()</span>
<span class="c"># I am the function which needs some decoration to remove my foul smell</span>
<span class="c"># I am doing some boring work after executing a_function_requiring_decoration()</span>
</pre></div>
</div>
<p>Did you get it? We just applied the previously learned principles. This
is exactly what the decorators do in python! They wrap a function and
modify its behaviour in one way or the another. Now you might be
wondering that we did not use the @ anywhere in our code? That is just a
short way of making up a decorated function. Here is how we could have
run the previous code sample using @.</p>
<div class="highlight-python"><div class="highlight"><pre><span class="nd">@a_new_decorator</span>
<span class="k">def</span> <span class="nf">a_function_requiring_decoration</span><span class="p">():</span>
<span class="sd">"""Hey yo! Decorate me!"""</span>
<span class="k">print</span> <span class="s">"I am the function which needs some decoration to </span><span class="se">\</span>
<span class="s"> remove my foul smell"</span>
<span class="n">a_function_requiring_decoration</span><span class="p">()</span>
<span class="c">#outputs: I am doing some boring work before executing a_function_requiring_decoration()</span>
<span class="c"># I am the function which needs some decoration to remove my foul smell</span>
<span class="c"># I am doing some boring work after executing a_function_requiring_decoration()</span>
<span class="c">#the @a_new_decorator is just a short way of saying:</span>
<span class="n">a_function_requiring_decoration</span> <span class="o">=</span> <span class="n">a_new_decorator</span><span class="p">(</span><span class="n">a_function_requiring_decoration</span><span class="p">)</span>
</pre></div>
</div>
<p>I hope you now have a basic understanding of how decorators work in
Python. Now there is one problem with our code. If we run:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">print</span><span class="p">(</span><span class="n">a_function_requiring_decoration</span><span class="o">.</span><span class="n">__name__</span><span class="p">)</span>
<span class="c"># Output: wrapTheFunction</span>
</pre></div>
</div>
<p>That’s not what we expected! It’s name is
“a_function_requiring_decoration”. Well our function was replaced by
wrapTheFunction. It overrided the name and docstring of our function.
Luckily Python provides us a simple function to solve this problem and
that is <code class="docutils literal"><span class="pre">functools.wraps</span></code>. Let’s modify our previous example to use
<code class="docutils literal"><span class="pre">functools.wraps</span></code>:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">wraps</span>
<span class="k">def</span> <span class="nf">a_new_decorator</span><span class="p">(</span><span class="n">a_func</span><span class="p">):</span>
<span class="nd">@wraps</span><span class="p">(</span><span class="n">a_func</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">wrapTheFunction</span><span class="p">():</span>
<span class="k">print</span> <span class="s">"I am doing some boring work before executing a_func()"</span>
<span class="n">a_func</span><span class="p">()</span>
<span class="k">print</span> <span class="s">"I am doing some boring work after executing a_func()"</span>
<span class="k">return</span> <span class="n">wrapTheFunction</span>
<span class="nd">@a_new_decorator</span>
<span class="k">def</span> <span class="nf">a_function_requiring_decoration</span><span class="p">():</span>
<span class="sd">"""Hey yo! Decorate me!"""</span>
<span class="k">print</span> <span class="s">"I am the function which needs some decoration to </span><span class="se">\</span>
<span class="s"> remove my foul smell"</span>
<span class="k">print</span><span class="p">(</span><span class="n">a_function_requiring_decoration</span><span class="o">.</span><span class="n">__name__</span><span class="p">)</span>
<span class="c"># Output: a_function_requiring_decoration</span>
</pre></div>
</div>
<p>Now that is much better. Let’s move on and learn some use-cases of
decorators.</p>
<p><strong>Blueprint :</strong></p>
<div class="code python highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">wraps</span>
<span class="k">def</span> <span class="nf">decorator_name</span><span class="p">(</span><span class="n">f</span><span class="p">):</span>
<span class="nd">@wraps</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">decorated</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">can_run</span><span class="p">:</span>
<span class="k">return</span> <span class="s">"Function will not run"</span>
<span class="k">return</span> <span class="n">f</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
<span class="k">return</span> <span class="n">decorated</span>
<span class="nd">@decorator_name</span>
<span class="k">def</span> <span class="nf">func</span><span class="p">():</span>
<span class="k">print</span> <span class="s">"Function is running"</span>
<span class="n">can_run</span> <span class="o">=</span> <span class="bp">True</span>
<span class="k">print</span><span class="p">(</span><span class="n">func</span><span class="p">())</span>
<span class="c"># Output: Function is running</span>
<span class="n">can_run</span><span class="o">=</span><span class="bp">False</span>
<span class="k">print</span><span class="p">(</span><span class="n">func</span><span class="p">())</span>
<span class="c"># Output: Function will not run</span>
</pre></div>
</div>
<p>Note: <code class="docutils literal"><span class="pre">@wraps</span></code> takes a function to be decorated and adds the
functionality of copying over the function name, docstring, arguments
list, etc. This allows to access the pre-decorated function’s properties
in the decorator.</p>
<div class="section" id="use-cases">
<h3>Use-cases:</h3>
<p>Now let’s take a look at the areas where decorators really shine and
their usage makes something really easy to manage.</p>
</div>
</div>
<div class="section" id="authorization">
<h2>1. Authorization</h2>
<p>Decorators can help to check whether someone is authorized to use an
endpoint in a web application. They are extensively used in Flask web
framework and Django. Here is an example to employ decorator based
authentication:</p>
<p><strong>Example :</strong></p>
<div class="code python highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">wraps</span>
<span class="k">def</span> <span class="nf">requires_auth</span><span class="p">(</span><span class="n">f</span><span class="p">):</span>
<span class="nd">@wraps</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">decorated</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="n">auth</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">authorization</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">auth</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">check_auth</span><span class="p">(</span><span class="n">auth</span><span class="o">.</span><span class="n">username</span><span class="p">,</span> <span class="n">auth</span><span class="o">.</span><span class="n">password</span><span class="p">):</span>
<span class="k">return</span> <span class="n">authenticate</span><span class="p">()</span>
<span class="k">return</span> <span class="n">f</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
<span class="k">return</span> <span class="n">decorated</span>
</pre></div>
</div>
</div>
<div class="section" id="logging">
<h2>2. Logging</h2>
<p>Logging is another area where the decorators shine. Here is an example:</p>
<div class="code python highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">wraps</span>
<span class="k">def</span> <span class="nf">logit</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
<span class="nd">@wraps</span><span class="p">(</span><span class="n">func</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">with_logging</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="k">print</span> <span class="n">func</span><span class="o">.</span><span class="n">__name__</span> <span class="o">+</span> <span class="s">" was called"</span>
<span class="k">return</span> <span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
<span class="k">return</span> <span class="n">with_logging</span>
<span class="nd">@logit</span>
<span class="k">def</span> <span class="nf">addition_func</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
<span class="sd">"""does some math"""</span>
<span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="n">x</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">addition_func</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
<span class="c"># Output: addition_func was called</span>
</pre></div>
</div>
<p>I am sure you are already thinking about some clever uses of decorators.</p>
</div>
</div>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="footer" role="contentinfo">
© Copyright 2015, Muhammad Yasoob Ullah Khalid.
</div>
</body>
</html>