0% found this document useful (0 votes)
2 views10 pages

Advanced Python Made Easy - Quick Code - Medium

The document discusses advanced features of Python, including list comprehension, slicing, lambda functions, map, filter, iteration protocol, generators, and generator expressions. It provides syntax examples and code snippets to illustrate each feature, highlighting their utility and efficiency. The document emphasizes the ease of use and memory efficiency of generators compared to traditional functions.

Uploaded by

abdullahpgdhrm
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views10 pages

Advanced Python Made Easy - Quick Code - Medium

The document discusses advanced features of Python, including list comprehension, slicing, lambda functions, map, filter, iteration protocol, generators, and generator expressions. It provides syntax examples and code snippets to illustrate each feature, highlighting their utility and efficiency. The document emphasizes the ease of use and memory efficiency of generators compared to traditional functions.

Uploaded by

abdullahpgdhrm
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

24/08/2019 Advanced Python made easy - Quick Code - Medium

Advanced Python made easy


Ravindra Parmar
Sep 6, 2018 · 5 min read

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.

List comprehension
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.

Syntax :-

[Link] 1/10
24/08/2019 Advanced Python made easy - Quick Code - Medium

[some_operation(element) for element in sequence]


- returns list of elements.

Example :-

1 # Python program to segregate positive and negative numbers in an array.


2
3 def segregate(arr):
4 return [x for x in arr if x%2 == 0] + [x for x in arr if x%2 != 0]
5
6 if __name__ == '__main__':
7 arr = [1, 8, 5, 3, 2, 6, 7, 10]
8 arr = segregate(arr)
9 print (arr)
10 # prints [8, 2, 6, 10, 1, 5, 3, 7]

[Link] hosted with ❤ by GitHub view raw

List comprehension example

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.

Slicing
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.

Syntax :-

list[start_index : end_index : step_size]


- returns list of elements.
- default start_index is 0.
- default end_index is -1.
- default step_size is 1.

Example :-

1 # Python program to rotate an array by 'd' elements.

[Link] 2/10
24/08/2019 Advanced Python made easy - Quick Code - Medium

2
3 def rotate(arr, d):
4 return arr[d:] + arr[:d]
5
6 if __name__ == '__main__':
7 arr = [1, 2, 3, 4, 5, 6, 7, 8]
8 arr = rotate(arr, 3)
9 print (arr)
10 # prints [3 ,4, 5, 6, 7, 8, 1, 2]

[Link] hosted with ❤ by GitHub view raw

List slicing example with step size as 1

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’.

1 # Python program to reverse an array.


2
3 def reverse(arr):
4 return arr[::-1]
5
6 if __name__ == '__main__':
7 arr = [1, 2, 3, 4, 5, 6, 7, 8]
8 arr = reverse(arr)
9 print (arr)
10 # prints [8 ,7, 6, 5, 4, 3, 2, 1]
11
12

[Link] hosted with ❤ by GitHub view raw

Another example showing the use case for step_size. Step size of -1 means slicing
would be from end to start.

Lambda
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.

Syntax :-

lambda arguments : expression

[Link] 3/10
24/08/2019 Advanced Python made easy - Quick Code - Medium

Example :-

1 import math
2
3 square_root = lambda x: [Link](x)
4 # is an equivalant lambda expression for below function
5 def square_root(x):
6 return [Link](x)

[Link] hosted with ❤ by GitHub view raw

Lambda expression for square root.

Map
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.

Syntax :-

map(function , sequence)
- returns an iterable.

Example :-

1 # Square the numbers in the list.


2 import math
3
4 if __name__ == '__main__':
5 arr = [1, 2, 3, 4, 5]
6 arr = list(map(lambda x : x**2, arr))
7 print (arr)
8 # prints [1, 4, 9, 16, 25]

[Link] hosted with ❤ by GitHub view raw

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).

Filter

[Link] 4/10
24/08/2019 Advanced Python made easy - Quick Code - Medium

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.

Syntax :-

filter(function, sequence)
- returns an iterable.

Example :-

1 # Print all even numbers in an array.


2
3 if __name__ == '__main__':
4 arr = [1, 2, 3, 4, 5, 6]
5 arr = list(filter(lambda x : x%2 == 0, arr))
6 print (arr)
7 # print [2, 4, 6]

[Link] hosted with ❤ by GitHub view raw

Here, we applied filter to return only the even numbers in the sequence.

Iteration protocol
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 [Link] with following content :-

1 import os
2 print ([Link])
3 print ([Link]())

[Link] hosted with ❤ by GitHub view raw

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.

1 file_obj = open('[Link]')
2
3 file [Link]()
[Link] 5/10
24/08/2019 Advanced Python made easy - Quick Code - Medium
3 file_obj.readline()
4 # 'import os' # first line.
5 file_obj.readline()
6 # 'print ([Link])' # next line.
7 file_obj.readline()
8 # 'print ([Link]())' # next line.
9 file_obj.readline()
10 # '' # empty string at the end of file.

[Link] hosted with ❤ by GitHub view raw

Another preferred and more efficient way is to use a for loop :-

1 # Reading file using 'for loop' based on iteration protocol.


2
3 for line in open('[Link]')
4 print ('line')

[Link] hosted with ❤ by GitHub view raw

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.

Well, here comes the iteration protocol. It’s summarized as below

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.

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).

1
2 file_obj = open('[Link]')
3
4 file_obj.__next__()
5 # 'import os'
6 file_obj.__next__()
7 # 'print ([Link])'
8 file_obj.__next__()
9 # 'print ([Link]())'
10 file_obj.__next__()
11 # Traceback (most recent call last):
[Link] 6/10
24/08/2019 Advanced Python made easy - Quick Code - Medium
( )
12 # File "<stdin>", line 1, in <module>
13 # StopIteration
14

iteration_protocol.py hosted with ❤ by GitHub view raw

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.

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.

1 L = [1,2,3]
2
3 I = iter(L)
4 print (I.__next__())
5 # '1'
6 print (I.__next__())
7 # '2'
8 print (I.__next__())
9 # '3'
10 print (I.__next__())
11 # Traceback (most recent call last):
12 # StopIteration

[Link] hosted with ❤ by GitHub view raw

Generators
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

Example :-

1 # Iterator for next power of two.


2 class NextPowTwo:
3 def __init__(self, max_ele = 0):
4 self.max_ele = max_ele
5
[Link] 7/10
24/08/2019 Advanced Python made easy - Quick Code - Medium
5
6 def __iter__(self):
7 self.n = 0
8 return self
9
10 def __next__(self):
11 if self.n <= self.max_ele:
12 result = 2 ** self.n
13 self.n += 1
14 return result
15 else:
16 raise StopIteration
17
18 if __name__ == '__main__':
19 it = iter(NextPowTwo(20))
20 print (next(it)) # prints '1'
21 print (next(it)) # prints '2'
22 print (next(it)) # prints '4'
23 print (next(it)) # prints '8'

[Link] hosted with ❤ by GitHub view raw

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.

1 # Generator for next power of two.


2 def NextPowTwo(max_ele):
3 n = 0
4 while n < max_ele:
5 yield 2 * n
6 n += 1
7 raise StopIteration
8
9 obj = NextPowTwo(20)
10 print ([Link]())
11 print ([Link]())
12 print ([Link]())
13 print ([Link]())

[Link] hosted with ❤ by GitHub view raw

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.

[Link] 8/10
24/08/2019 Advanced Python made easy - Quick Code - Medium

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.

Essentially generator,

has at least one yield statement.

returns an object (iterator) but does not start execution immediately.

remembers the local variables and their states between successive calls.

implements the iteration protocol.

Generator Expression
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.

Example :-

1 # generator function example.


2
3 def func():
4 n = 1
5 while n < 25:
6 yield n**n
7 n += 1
8
9 if __name__ == '__main__':
10 it = func()
11 print (next(it))
12 print (next(it))
13 print (next(it))

[Link] hosted with ❤ by GitHub view raw

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.

Why generators in python?

[Link] 9/10
24/08/2019 Advanced Python made easy - Quick Code - Medium

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

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

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.

Please let me know through your comments any modifications/improvements needed in


the article.

Python Python3 Algorithms Programming Coding

About Help Legal

[Link] 10/10

You might also like