|
1 Functional Approach |
|
2 =================== |
|
3 |
|
4 *Functions* allow us to enclose a set of statements and call the function again |
|
5 and again instead of repeating the group of statements everytime. Functions also |
|
6 allow us to isolate a piece of code from all the other code and provides the |
|
7 convenience of not polluting the global variables. |
|
8 |
|
9 *Function* in python is defined with the keyword **def** followed by the name |
|
10 of the function, in turn followed by a pair of parenthesis which encloses the |
|
11 list of parameters to the function. The definition line ends with a ':'. The |
|
12 definition line is followed by the body of the function intended by one block. |
|
13 The *Function* must return a value:: |
|
14 |
|
15 def factorial(n): |
|
16 fact = 1 |
|
17 for i in range(2, n): |
|
18 fact *= i |
|
19 |
|
20 return fact |
|
21 |
|
22 The code snippet above defines a function with the name factorial, takes the |
|
23 number for which the factorial must be computed, computes the factorial and |
|
24 returns the value. |
|
25 |
|
26 A *Function* once defined can be used or called anywhere else in the program. We |
|
27 call a fucntion with its name followed by a pair of parenthesis which encloses |
|
28 the arguments to the function. |
|
29 |
|
30 The value that function returns can be assigned to a variable. Let's call the |
|
31 above function and store the factorial in a variable:: |
|
32 |
|
33 fact5 = factorial(5) |
|
34 |
|
35 The value of fact5 will now be 120, which is the factorial of 5. Note that we |
|
36 passed 5 as the argument to the function. |
|
37 |
|
38 It may be necessary to document what the function does, for each of the function |
|
39 to help the person who reads our code to understand it better. In order to do |
|
40 this Python allows the first line of the function body to be a string. This |
|
41 string is called as *Documentation String* or *docstring*. *docstrings* prove |
|
42 to be very handy since there are number of tools which can pull out all the |
|
43 docstrings from Python functions and generate the documentation automatically |
|
44 from it. *docstrings* for functions can be written as follows:: |
|
45 |
|
46 def factorial(n): |
|
47 'Returns the factorial for the number n.' |
|
48 fact = 1 |
|
49 for i in range(2, n): |
|
50 fact *= i |
|
51 |
|
52 return fact |
|
53 |
|
54 An important point to note at this point is that, a function can return any |
|
55 Python value or a Python object, which also includes a *Tuple*. A *Tuple* is |
|
56 just a collection of values and those values themselves can be of any other |
|
57 valid Python datatypes, including *Lists*, *Tuples*, *Dictionaries* among other |
|
58 things. So effectively, if a function can return a tuple, it can return any |
|
59 number of values through a tuple |
|
60 |
|
61 Let us write a small function to swap two values:: |
|
62 |
|
63 def swap(a, b): |
|
64 return b, a |
|
65 |
|
66 c, d = swap(a, b) |
|
67 |
|
68 Function scope |
|
69 --------------- |
|
70 The variables used inside the function are confined to the function's scope |
|
71 and doesn't pollute the variables of the same name outside the scope of the |
|
72 function. Also the arguments passed to the function are passed by-value if |
|
73 it is of basic Python data type:: |
|
74 |
|
75 def cant_change(n): |
|
76 n = 10 |
|
77 |
|
78 n = 5 |
|
79 cant_change(n) |
|
80 |
|
81 Upon running this code, what do you think would have happened to value of n |
|
82 which was assigned 5 before the function call? If you have already tried out |
|
83 that snippet on the interpreter you already know that the value of n is not |
|
84 changed. This is true of any immutable types of Python like *Numbers*, *Strings* |
|
85 and *Tuples*. But when you pass mutable objects like *Lists* and *Dictionaries* |
|
86 the values are manipulated even outside the function:: |
|
87 |
|
88 >>> def can_change(n): |
|
89 ... n[1] = James |
|
90 ... |
|
91 |
|
92 >>> name = ['Mr.', 'Steve', 'Gosling'] |
|
93 >>> can_change(name) |
|
94 >>> name |
|
95 ['Mr.', 'James', 'Gosling'] |
|
96 |
|
97 If nothing is returned by the function explicitly, Python takes care to return |
|
98 None when the funnction is called. |
|
99 |
|
100 Default Arguments |
|
101 ----------------- |
|
102 |
|
103 There may be situations where we need to allow the functions to take the |
|
104 arguments optionally. Python allows us to define function this way by providing |
|
105 a facility called *Default Arguments*. For example, we need to write a function |
|
106 that returns a list of fibonacci numbers. Since our function cannot generate an |
|
107 infinite list of fibonacci numbers, we need to specify the number of elements |
|
108 that the fibonacci sequence must contain. Suppose, additionally, we want to the |
|
109 function to return 10 numbers in the sequence if no option is specified we can |
|
110 define the function as follows:: |
|
111 |
|
112 def fib(n=10): |
|
113 fib_list = [0, 1] |
|
114 for i in range(n - 2): |
|
115 next = fib_list[-2] + fib_list[-1] |
|
116 fib_list.append(next) |
|
117 return fib_list |
|
118 |
|
119 When we call this function, we can optionally specify the value for the |
|
120 parameter n, during the call as an argument. Calling with no argument and |
|
121 argument with n=5 returns the following fibonacci sequences:: |
|
122 |
|
123 fib() |
|
124 [0, 1, 1, 2, 3, 5, 8, 13, 21, 34] |
|
125 fib(5) |
|
126 [0, 1, 1, 2, 3] |
|
127 |
|
128 Keyword Arguments |
|
129 ----------------- |
|
130 |
|
131 When a function takes a large number of arguments, it may be difficult to |
|
132 remember the order of the parameters in the function definition or it may |
|
133 be necessary to pass values to only certain parameters since others take |
|
134 the default value. In either of these cases, Python provides the facility |
|
135 of passing arguments by specifying the name of the parameter as defined in |
|
136 the function definition. This is known as *Keyword Arguments*. |
|
137 |
|
138 In a function call, *Keyword arguments* can be used for each argument, in the |
|
139 following fashion:: |
|
140 |
|
141 argument_name=argument_value |
|
142 Also denoted as: keyword=argument |
|
143 |
|
144 def wish(name='World', greetings='Hello'): |
|
145 print "%s, %s!" % (greetings, name) |
|
146 |
|
147 This function can be called in one of the following ways. It is important to |
|
148 note that no restriction is imposed in the order in which *Keyword arguments* |
|
149 can be specified. Also note, that we have combined *Keyword arguments* with |
|
150 *Default arguments* in this example, however it is not necessary:: |
|
151 |
|
152 wish(name='Guido', greetings='Hey') |
|
153 wish(greetings='Hey', name='Guido') |
|
154 |
|
155 Calling functions by specifying arguments in the order of parameters specified |
|
156 in the function definition is called as *Positional arguments*, as opposed to |
|
157 *Keyword arguments*. It is possible to use both *Positional arguments* and |
|
158 *Keyword arguments* in a single function call. But Python doesn't allow us to |
|
159 bungle up both of them. The arguments to the function, in the call, must always |
|
160 start with *Positional arguments* which is in turn followed by *Keyword |
|
161 arguments*:: |
|
162 |
|
163 def my_func(x, y, z, u, v, w): |
|
164 # initialize variables. |
|
165 ... |
|
166 # do some stuff |
|
167 ... |
|
168 # return the value |
|
169 |
|
170 It is valid to call the above functions in the following ways:: |
|
171 |
|
172 my_func(10, 20, 30, u=1.0, v=2.0, w=3.0) |
|
173 my_func(10, 20, 30, 1.0, 2.0, w=3.0) |
|
174 my_func(10, 20, z=30, u=1.0, v=2.0, w=3.0) |
|
175 my_func(x=10, y=20, z=30, u=1.0, v=2.0, w=3.0) |
|
176 |
|
177 Following lists some of the invalid calls:: |
|
178 |
|
179 my_func(10, 20, z=30, 1.0, 2.0, 3.0) |
|
180 my_func(x=10, 20, z=30, 1.0, 2.0, 3.0) |
|
181 my_func(x=10, y=20, z=30, u=1.0, v=2.0, 3.0) |
|
182 |
|
183 Parameter Packing and Unpacking |
|
184 ------------------------------- |
|
185 |
|
186 The positional arguments passed to a function can be collected in a tuple |
|
187 parameter and keyword arguments can be collected in a dictionary. Since keyword |
|
188 arguments must always be the last set of arguments passed to a function, the |
|
189 keyword dictionary parameter must be the last parameter. The function definition |
|
190 must include a list explicit parameters, followed by tuple paramter collecting |
|
191 parameter, whose name is preceded by a *****, for collecting positional |
|
192 parameters, in turn followed by the dictionary collecting parameter, whose name |
|
193 is preceded by a ****** :: |
|
194 |
|
195 def print_report(title, *args, **name): |
|
196 """Structure of *args* |
|
197 (age, email-id) |
|
198 Structure of *name* |
|
199 { |
|
200 'first': First Name |
|
201 'middle': Middle Name |
|
202 'last': Last Name |
|
203 } |
|
204 """ |
|
205 |
|
206 print "Title: %s" % (title) |
|
207 print "Full name: %(first)s %(middle)s %(last)s" % name |
|
208 print "Age: %d\nEmail-ID: %s" % args |
|
209 |
|
210 The above function can be called as. Note, the order of keyword parameters can |
|
211 be interchanged:: |
|
212 |
|
213 >>> print_report('Employee Report', 29, 'johny@example.com', first='Johny', |
|
214 last='Charles', middle='Douglas') |
|
215 Title: Employee Report |
|
216 Full name: Johny Douglas Charles |
|
217 Age: 29 |
|
218 Email-ID: johny@example.com |
|
219 |
|
220 The reverse of this can also be achieved by using a very identical syntax while |
|
221 calling the function. A tuple or a dictionary can be passed as arguments in |
|
222 place of a list of *Positional arguments* or *Keyword arguments* respectively |
|
223 using ***** or ****** :: |
|
224 |
|
225 def print_report(title, age, email, first, middle, last): |
|
226 print "Title: %s" % (title) |
|
227 print "Full name: %s %s %s" % (first, middle, last) |
|
228 print "Age: %d\nEmail-ID: %s" % (age, email) |
|
229 |
|
230 >>> args = (29, 'johny@example.com') |
|
231 >>> name = { |
|
232 'first': 'Johny', |
|
233 'middle': 'Charles', |
|
234 'last': 'Douglas' |
|
235 } |
|
236 >>> print_report('Employee Report', *args, **name) |
|
237 Title: Employee Report |
|
238 Full name: Johny Charles Douglas |
|
239 Age: 29 |
|
240 Email-ID: johny@example.com |
|
241 |
|
242 Nested Functions and Scopes |
|
243 --------------------------- |
|
244 |
|
245 Python allows nesting one function inside another. This style of programming |
|
246 turns out to be extremely flexible and powerful features when we use *Python |
|
247 decorators*. We will not talk about decorators is beyond the scope of this |
|
248 course. If you are interested in knowing more about *decorator programming* in |
|
249 Python you are suggested to read: |
|
250 |
|
251 | http://avinashv.net/2008/04/python-decorators-syntactic-sugar/ |
|
252 | http://personalpages.tds.net/~kent37/kk/00001.html |
|
253 |
|
254 However, the following is an example for nested functions in Python:: |
|
255 |
|
256 def outer(): |
|
257 print "Outer..." |
|
258 def inner(): |
|
259 print "Inner..." |
|
260 print "Outer..." |
|
261 inner() |
|
262 |
|
263 >>> outer() |
|
264 |
|
265 map, reduce and filter functions |
|
266 -------------------------------- |
|
267 |
|
268 Python provides several built-in functions for convenience. The **map()**, |
|
269 **reduce()** and **filter()** functions prove to be very useful with sequences like |
|
270 *Lists*. |
|
271 |
|
272 The **map** (*function*, *sequence*) function takes two arguments: *function* |
|
273 and a *sequence* argument. The *function* argument must be the name of the |
|
274 function which in turn takes a single argument, the individual element of the |
|
275 *sequence*. The **map** function calls *function(item)*, for each item in the |
|
276 sequence and returns a list of values, where each value is the value returned |
|
277 by each call to *function(item)*. **map()** function allows to pass more than |
|
278 one sequence. In this case, the first argument, *function* must take as many |
|
279 arguments as the number of sequences passed. This function is called with each |
|
280 corresponding element in the each of the sequences, or **None** if one of the |
|
281 sequence is exhausted:: |
|
282 |
|
283 def square(x): |
|
284 return x*x |
|
285 |
|
286 >>> map(square, [1, 2, 3, 4]) |
|
287 [1, 4, 9, 16] |
|
288 |
|
289 def mul(x, y): |
|
290 return x*y |
|
291 |
|
292 >>> map(mul, [1, 2, 3, 4], [6, 7, 8, 9]) |
|
293 |
|
294 The **filter** (*function*, *sequence*) function takes two arguments, similar to |
|
295 the **map()** function. The **filter** function calls *function(item)*, for each |
|
296 item in the sequence and returns all the elements in the sequence for which |
|
297 *function(item)* returned True:: |
|
298 |
|
299 def even(x): |
|
300 if x % 2: |
|
301 return True |
|
302 else: |
|
303 return False |
|
304 |
|
305 >>> filter(even, range(1, 10)) |
|
306 [1, 3, 5, 7, 9] |
|
307 |
|
308 The **reduce** (*function*, *sequence*) function takes two arguments, similar to |
|
309 **map** function, however multiple sequences are not allowed. The **reduce** |
|
310 function calls *function* with first two consecutive elements in the sequence, |
|
311 obtains the result, calls *function* with the result and the subsequent element |
|
312 in the sequence and so on until the end of the list and returns the final result:: |
|
313 |
|
314 def mul(x, y): |
|
315 return x*y |
|
316 |
|
317 >>> reduce(mul, [1, 2, 3, 4]) |
|
318 24 |
|
319 |
|
320 List Comprehensions |
|
321 ~~~~~~~~~~~~~~~~~~~ |
|
322 |
|
323 List Comprehension is a convenvience utility provided by Python. It is a |
|
324 syntatic sugar to create *Lists*. Using *List Comprehensions* one can create |
|
325 *Lists* from other type of sequential data structures or other *Lists* itself. |
|
326 The syntax of *List Comprehensions* consists of a square brackets to indicate |
|
327 the result is a *List* within which we include at least one **for** clause and |
|
328 multiple **if** clauses. It will be more clear with an example:: |
|
329 |
|
330 >>> num = [1, 2, 3] |
|
331 >>> sq = [x*x for x in num] |
|
332 >>> sq |
|
333 [1, 4, 9] |
|
334 >>> all_num = [1, 2, 3, 4, 5, 6, 7, 8, 9] |
|
335 >>> even = [x for x in all_num if x%2 == 0] |
|
336 |
|
337 The syntax used here is very clear from the way it is written. It can be |
|
338 translated into english as, "for each element x in the list all_num, |
|
339 if remainder of x divided by 2 is 0, add x to the list." |