Python/Advanced Python made easy.md
... ...
@@ -0,0 +1,335 @@
1
+https://medium.com/quick-code/advanced-python-made-easy-eece317334fa
2
+
3
+# Advanced Python made easy. Python is an object-orientated language… | by Ravindra Parmar | Quick Code | Medium
4
+
5
+![Ravindra Parmar](https://miro.medium.com/v2/resize:fill:88:88/1*Vos_j4_B1aPm0Ddm_Gvv4g.png)
6
+[Ravindra Parmar](https://medium.com/@ravindra089)
7
+
8
+
9
+![Quick Code](https://miro.medium.com/v2/resize:fill:48:48/1*Ccl9cej4i8GIiurZIOaB9g.png)
10
+[Quick Code](https://medium.com/quick-code)
11
+
12
+Python is an object-orientated language that closely resembles the English language which makes it a great language to learn for beginners. It’s advanced features and package of supported libraries even makes hard task be writable in bunch of lines of code. In this articles we’ll go through few advanced features of python.
13
+
14
+**List comprehension**
15
+----------------------
16
+
17
+List comprehension provides a short and better alternative to ubiquitous _for loops_. It is used in context of iterations where we need to perform an operation on every element of the list.
18
+
19
+**Syntax :-**
20
+
21
+```
22
+ [some_operation(element) for element in sequence]
23
+ - returns list of elements.
24
+```
25
+
26
+
27
+**Example :-**
28
+```
29
+# Python program to segregate positive and negative numbers in an array.
30
+
31
+def segregate(arr):
32
+ return [x for x in arr if x%2 == 0] + [x for x in arr if x%2 != 0]
33
+
34
+if __name__ == '__main__':
35
+ arr = [1, 8, 5, 3, 2, 6, 7, 10]
36
+ arr = segregate(arr)
37
+ print (arr)
38
+ # prints [8, 2, 6, 10, 1, 5, 3, 7]
39
+```
40
+
41
+List comprehension example
42
+
43
+We are concatenating the lists returned from two list comprehensions. First one is applying an _even check_ on every element of list whereas second one performs an _odd check_.
44
+
45
+**Slicing**
46
+-----------
47
+
48
+Slicing is used to extract a continuous sequence/sub sequence of elements from a given sequence. By default _step\_size_ is one and hence generating a continuous sequence. However, we can provide any value for _step\_size_ to get non-continuous sequence of elements.
49
+
50
+**Syntax :-**
51
+
52
+```
53
+ list[start_index : end_index : step_size]
54
+ - returns list of elements.
55
+ - default start_index is 0.
56
+ - default end_index is -1.
57
+ - default step_size is 1.
58
+```
59
+
60
+
61
+**Example :-**
62
+```
63
+# Python program to rotate an array by 'd' elements.
64
+
65
+def rotate(arr, d):
66
+ return arr[d:] + arr[:d]
67
+
68
+if __name__ == '__main__':
69
+ arr = [1, 2, 3, 4, 5, 6, 7, 8]
70
+ arr = rotate(arr, 3)
71
+ print (arr)
72
+ # prints [3 ,4, 5, 6, 7, 8, 1, 2]
73
+```
74
+
75
+List slicing example with step size as 1
76
+
77
+Here again, we are concatenating the results of two slicing operations. First, we are slicing the list from index ‘d’ to end, then from start to index ‘d’.
78
+
79
+```
80
+# Python program to reverse an array.
81
+
82
+def reverse(arr):
83
+ return arr[::-1]
84
+
85
+if __name__ == '__main__':
86
+ arr = [1, 2, 3, 4, 5, 6, 7, 8]
87
+ arr = reverse(arr)
88
+ print (arr)
89
+ # prints [8 ,7, 6, 5, 4, 3, 2, 1]
90
+
91
+```
92
+
93
+Another example showing the use case for step\_size. Step size of -1 means slicing would be from end to start.
94
+
95
+**Lambda**
96
+----------
97
+
98
+Lambda is an anonymous function with capability of holding a single expression only. It’s basically a shorthand for functions and can be used anywhere an expression is needed.
99
+
100
+**Syntax :-**
101
+
102
+```
103
+ lambda arguments : expression
104
+```
105
+
106
+
107
+**Example :-**
108
+```
109
+import math
110
+
111
+square_root = lambda x: math.sqrt(x)
112
+# is an equivalant lambda expression for below function
113
+def square_root(x):
114
+ return math.sqrt(x)
115
+```
116
+
117
+Lambda expression for square root.
118
+
119
+Map
120
+---
121
+
122
+Map is used in scenarios where we need to apply a function/lambda over a sequence of elements. Although you can almost always replace the need for using a map with list comprehensions.
123
+
124
+**Syntax :-**
125
+
126
+```
127
+ map(function , sequence)
128
+ - returns an iterable.
129
+```
130
+
131
+
132
+**Example :-**
133
+```
134
+# Square the numbers in the list.
135
+import math
136
+
137
+if __name__ == '__main__':
138
+ arr = [1, 2, 3, 4, 5]
139
+ arr = list(map(lambda x : x**2, arr))
140
+ print (arr)
141
+ # prints [1, 4, 9, 16, 25]
142
+```
143
+
144
+_Map_ is used to square every element of the sequence. As _map_ returns an iterable, we need to wrap the result with desired type (_list_ in above example).
145
+
146
+Filter
147
+------
148
+
149
+Filter, on the other hand, applies a function/lambda over a sequence of elements and returns the sequence of elements for which function/lambda returned _True_.
150
+
151
+**Syntax :-**
152
+
153
+```
154
+ filter(function, sequence)
155
+ - returns an iterable.
156
+```
157
+
158
+
159
+**Example :-**
160
+```
161
+# Print all even numbers in an array.
162
+
163
+if __name__ == '__main__':
164
+ arr = [1, 2, 3, 4, 5, 6]
165
+ arr = list(filter(lambda x : x%2 == 0, arr))
166
+ print (arr)
167
+ # print [2, 4, 6]
168
+```
169
+
170
+Here, we applied filter to return only the even numbers in the sequence. It is highly recommended to [learn Python](https://blog.coursesity.com/learning-plan-learn-coding-in-python-like-a-tiger/) programming in 2021.
171
+
172
+Iteration protocol
173
+------------------
174
+
175
+An important concept which permeates the python programming language is iteration protocol and iterables. In simplest terms, an iterable is something which could be iterated over using an iteration protocol. One of the easiest way to understand the iteration protocol is to see how it works with built-in types. Let’s take an example of file. The file we are going to use as sample is _script.py_ with following content _:-_
176
+```
177
+import os
178
+print (os.name)
179
+print (os.getcwd())
180
+```
181
+
182
+We have few many ways to read a file in python, some more efficient than others. One way, not in latter category, would be to use _readline_.
183
+```
184
+file_obj = open('script.py')
185
+
186
+file_obj.readline()
187
+# 'import os' # first line.
188
+file_obj.readline()
189
+# 'print (os.name)' # next line.
190
+file_obj.readline()
191
+# 'print (os.getcwd())' # next line.
192
+file_obj.readline()
193
+# '' # empty string at the end of file.
194
+```
195
+
196
+Another preferred and more efficient way is to use a for loop :-
197
+```
198
+# Reading file using 'for loop' based on iteration protocol.
199
+
200
+for line in open('script.py')
201
+ print ('line')
202
+```
203
+
204
+That’s just a line of code to read whole file. But how does it work? How the hell for loop knows to read file line over line.
205
+
206
+Well, here comes the iteration protocol. It’s summarized as below
207
+
208
+> Any object with a \_\_next\_\_ method to advance to next result and which raises StopIteration exception at the end of series of results, is considered an iterator in python. Any such object may also be stepped through with for loop or other iteration tool.
209
+
210
+In the above example, file object is itself an iterator (as it implements the desired interface) whereas for loop is an iteration tool. Below is the most of interface of what we call the iteration protocol in python (_most really is meaningful here as explained below_).
211
+
212
+```
213
+file_obj = open('script.py')
214
+
215
+file_obj.__next__()
216
+# 'import os'
217
+file_obj.__next__()
218
+# 'print (os.name)'
219
+file_obj.__next__()
220
+# 'print (os.getcwd())'
221
+file_obj.__next__()
222
+# Traceback (most recent call last):
223
+# File "<stdin>", line 1, in <module>
224
+# StopIteration
225
+```
226
+
227
+And that’s what internally for loop or in general any iteration tool will do i.e call _\_\_next\_\__ method until reaches end. Besides for loop there are other iteration tools in python such as _list comprehension, map, zip_ etc.
228
+
229
+So far so good, however there is one more step to iteration protocol and that is to get the iterator of the underlying object. _This step was not required for the file object as it’s its own iterator_. But for other object like list we need to go through this one extra step of retrieving the iterator.
230
+
231
+```
232
+L = [1,2,3]
233
+
234
+I = iter(L)
235
+print (I.__next__())
236
+# '1'
237
+print (I.__next__())
238
+# '2'
239
+print (I.__next__())
240
+# '3'
241
+print (I.__next__())
242
+# Traceback (most recent call last):
243
+# StopIteration
244
+```
245
+
246
+**Generators**
247
+--------------
248
+
249
+Generators are a simple way of creating iterators. More formally, generators are the functions that returns an object (iterator) which we can iterate over (one value at a time). If we were to write the same functionality from scratch in python, it would be something like
250
+
251
+**Example :-**
252
+```
253
+# Iterator for next power of two.
254
+class NextPowTwo:
255
+ def __init__(self, max_ele = 0):
256
+ self.max_ele = max_ele
257
+
258
+ def __iter__(self):
259
+ self.n = 0
260
+ return self
261
+
262
+ def __next__(self):
263
+ if self.n <= self.max_ele:
264
+ result = 2 ** self.n
265
+ self.n += 1
266
+ return result
267
+ else:
268
+ raise StopIteration
269
+
270
+if __name__ == '__main__':
271
+ it = iter(NextPowTwo(20))
272
+ print (next(it)) # prints '1'
273
+ print (next(it)) # prints '2'
274
+ print (next(it)) # prints '4'
275
+ print (next(it)) # prints '8'
276
+```
277
+
278
+However, python made it easy for us. Below is something similar using generators. As you can see, all the overhead mentioned above (_calling \_\_iter\_\_() and \_\_next\_\_()_)is automatically handled by generators.
279
+
280
+```
281
+# Generator for next power of two.
282
+def NextPowTwo(max_ele):
283
+ n = 0
284
+ while n < max_ele:
285
+ yield 2 * n
286
+ n += 1
287
+ raise StopIteration
288
+
289
+obj = NextPowTwo(20)
290
+print (obj.next())
291
+print (obj.next())
292
+print (obj.next())
293
+print (obj.next())
294
+```
295
+
296
+> _Generators are created by defining a normal function with yield statement instead of return statement_ i.e if the function contains at least one yield statement, it becomes a generator function. Both yield and return will return some value from the function. Whereas function terminates on execution of return, yield statement pauses the function, saving all it’s states and later continues from there on successive calls.
297
+
298
+Essentially generator,
299
+
300
+* has at least one yield statement.
301
+* returns an object (iterator) but does not start execution immediately.
302
+* remembers the local variables and their states between successive calls.
303
+* implements the iteration protocol.
304
+
305
+**Generator Expression**
306
+------------------------
307
+
308
+As lambda is to function , generator expression is to generator in python i.e generator expression creates an anonymous generator function. It’s syntax is much similar to list comprehension.
309
+
310
+**Example :-**
311
+```
312
+# generator function example.
313
+
314
+def func():
315
+ n = 1
316
+ while n < 25:
317
+ yield n**n
318
+ n += 1
319
+
320
+if __name__ == '__main__':
321
+ it = func()
322
+ print (next(it))
323
+ print (next(it))
324
+ print (next(it))
325
+```
326
+
327
+The major difference between list comprehension and generator is that while list comprehension produces the entire list, generator expression produces one item at a time. They are, in essence, kind of lazy counter parts of list comprehension.
328
+
329
+**Why generators in python?**
330
+
331
+* They are easy to implement. As you can see how we were able to convert tens of lines of code to just 3 line with the help of generators
332
+* They are extremely memory efficient. A normal function to return next power of two would create an entire sequence in memory. The cost would be significant in case of billions of numbers. Generators can implement them in more memory friendly manner as they generate single element at a time rather than whole sequence
333
+* Generators can also be used to represent infinite stream of data. Since infinite stream could not be stored in memory, generators proves useful in this scenario as well.
334
+
335
+_Please let me know through your comments any modifications/improvements needed in the article._
... ...
\ No newline at end of file