Comprehensions¶
Sometimes, it is useful to make some operations on Data Structures and return the same Data Structure. Examples may include squaring every element of a collection, constructing a lookup table and so on. Python provides an easier syntax for doing these tasks. Let’s understand comprehension techiniques by solving some problems
Problem 1¶
Given a list of integers, Create a new list containing their squares
Classic, C
like approach¶
In [3]:
num = [10,8,3,5,2,7,0,1,4,9,6]
num
Out[3]:
[10, 8, 3, 5, 2, 7, 0, 1, 4, 9, 6]
In [6]:
def square_classic_approach(x):
"""
Input: x - List of Integers
Return: A list containing squares of each element of list
"""
squared = [] # Empty list
for i in range(len(num)): # Equivalent to for(i=0;i<num;i++)
squared.append(num[i]**2) # Power operator
return squared
Note
Documentation of Python
code can be done using
docstringss
In [7]:
square_classic_approach(num)
Out[7]:
[100, 64, 9, 25, 4, 49, 0, 1, 16, 81, 36]
Comprehension Based Approach¶
In [13]:
def square_pythonic_approach(x): # Pythonic! :P
"""
Input: x - List of Integers
Return: A list containing squares of each element of list
"""
return [ num**2 for num in x ]
In [9]:
square_pythonic_approach(num)
Out[9]:
[100, 64, 9, 25, 4, 49, 0, 1, 16, 81, 36]
Note
Comprehension increases code readability. Comprehension can be applied to any collection.
Problem 2¶
Given a list of integers, square them if they are even number and return a list
Classic, C
like approach¶
In [23]:
def square_if_even_classic_approach(x):
"""
Input: x - List of Integers
Return: A list containing squares of each element of list if element is even
"""
squared = [] # Empty list
for i in range(len(x)): # Equivalent to for(i=0;i<num;i++)
if x[i] % 2 == 0:
squared.append(x[i]**2) # Power operator
else:
squared.append(x[i])
return squared
In [24]:
square_if_even_classic_approach([10,9,2,4,5,67])
Out[24]:
[100, 9, 4, 16, 5, 67]
Observe how list is created and passed on the fly
Comprehension based approach¶
In [16]:
def square_if_even_pythonic_approach(x): # Pythonic! :P
"""
Input: x - List of Integers
Return: A list containing squares of each element of list if element is even
"""
return [ num**2 if num % 2 == 0 else num for num in x ]
In [17]:
square_if_even_pythonic_approach(([10,9,2,4,5,67]))
Out[17]:
[100, 9, 4, 16, 5, 67]
Problem 3¶
Given a string, return the vowels occuring in it, ignoring the case
In [31]:
def vowels_in(string):
"""
Input: string - a string
Return: List of vowels occuring in string
Example:
>>> vowels_in('Apple`)
['a','e']
"""
# We use a set because it stores unique elements
l_string = str.lower(string) # Converting to unique form
vowel_set = {c for c in l_string if c in 'aeiou'} # Note the imposal of condition
return sorted(list(vowel_set))
In [29]:
vowels_in('Apple')
Out[29]:
['a', 'e']
In [30]:
vowels_in('Karnataka')
Out[30]:
['a']
Above function can be written in more compact form
In [32]:
def vowels_in_compact(string):
"""
Input: string - a string
Return: List of vowels occuring in string
Example:
>>> vowels_in('Apple`)
['a','e']
"""
return sorted(list({c for c in str.lower(string) if c in 'aeiou'}))
In [33]:
vowels_in_compact('violin')
Out[33]:
['i', 'o']
Problem 4¶
Given a string, count the number of occurance of each character, ignoring the case
The nature of the problem makes us to use the dicionary data structure.
In [36]:
def alphabet_occurance_count(string):
"""
Input: a string
Output: the number of occurance od f each character in the string
"""
return {x:string.count(x) for x in string}
In [38]:
alphabet_occurance_count("Hello from Notebook")
Out[38]:
{' ': 2,
'H': 1,
'N': 1,
'b': 1,
'e': 2,
'f': 1,
'k': 1,
'l': 2,
'm': 1,
'o': 5,
'r': 1,
't': 1}
Zen revisited¶
In the beginning of this chapter, we looked at Zen of Python. Now we inspect the things in ``this`` module In the ``this`` module of Python, ``this.s`` contains the encrypted text, which has to be decrypted using ``this.d``. Decode it
In [39]:
import this
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
Obviously, this is Zen
Now let’s have a look at other things in this module
In [40]:
this.c
Out[40]:
97
In [41]:
this.d
Out[41]:
{'A': 'N',
'B': 'O',
'C': 'P',
'D': 'Q',
'E': 'R',
'F': 'S',
'G': 'T',
'H': 'U',
'I': 'V',
'J': 'W',
'K': 'X',
'L': 'Y',
'M': 'Z',
'N': 'A',
'O': 'B',
'P': 'C',
'Q': 'D',
'R': 'E',
'S': 'F',
'T': 'G',
'U': 'H',
'V': 'I',
'W': 'J',
'X': 'K',
'Y': 'L',
'Z': 'M',
'a': 'n',
'b': 'o',
'c': 'p',
'd': 'q',
'e': 'r',
'f': 's',
'g': 't',
'h': 'u',
'i': 'v',
'j': 'w',
'k': 'x',
'l': 'y',
'm': 'z',
'n': 'a',
'o': 'b',
'p': 'c',
'q': 'd',
'r': 'e',
's': 'f',
't': 'g',
'u': 'h',
'v': 'i',
'w': 'j',
'x': 'k',
'y': 'l',
'z': 'm'}
It looks like a mapping from one character to another… Hmm… Interesting!
In [42]:
this.i
Out[42]:
25
In [43]:
this.s
Out[43]:
"Gur Mra bs Clguba, ol Gvz Crgref\n\nOrnhgvshy vf orggre guna htyl.\nRkcyvpvg vf orggre guna vzcyvpvg.\nFvzcyr vf orggre guna pbzcyrk.\nPbzcyrk vf orggre guna pbzcyvpngrq.\nSyng vf orggre guna arfgrq.\nFcnefr vf orggre guna qrafr.\nErnqnovyvgl pbhagf.\nFcrpvny pnfrf nera'g fcrpvny rabhtu gb oernx gur ehyrf.\nNygubhtu cenpgvpnyvgl orngf chevgl.\nReebef fubhyq arire cnff fvyragyl.\nHayrff rkcyvpvgyl fvyraprq.\nVa gur snpr bs nzovthvgl, ershfr gur grzcgngvba gb thrff.\nGurer fubhyq or bar-- naq cersrenoyl bayl bar --boivbhf jnl gb qb vg.\nNygubhtu gung jnl znl abg or boivbhf ng svefg hayrff lbh'er Qhgpu.\nAbj vf orggre guna arire.\nNygubhtu arire vf bsgra orggre guna *evtug* abj.\nVs gur vzcyrzragngvba vf uneq gb rkcynva, vg'f n onq vqrn.\nVs gur vzcyrzragngvba vf rnfl gb rkcynva, vg znl or n tbbq vqrn.\nAnzrfcnprf ner bar ubaxvat terng vqrn -- yrg'f qb zber bs gubfr!"
Wow!…Looks like encoded text.
We will decode it using this.d
mapping
In [47]:
decoded = ''.join([this.d[c] if str.isalnum(c) else c for c in this.s]) # join() appends a iterable to string
print(decoded)
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
This is again *Zen of Python!*
In fact ``this`` module uses a function to build the Translation
Table ``d`` in it’s __init.py__
to print the *Zen*. The
encoding done here is rot13
encoding. We will look about modules in
upcoming chapters.
Fibonacci Again¶
Now you have understood the lists and operations. Let’s look at a recursive Fibonacci Number Generator
In [53]:
def fibonacci_recursive(n,first=0,second=1):
return [] if n == 0 else [first] + fibonacci_recursive(n - 1, second, first + second)
In [54]:
fibonacci_recursive(5)
Out[54]:
[0, 1, 1, 2, 3]
Filtering Lists - Need for lambda
s¶
In Python, functions are also objects. It means that you can pass them
to other function like a variable. This flexibility of functions allows
us to do many useful tasks. filter
ing a collection is one of them.
Problem : Find even numbers in a given sequence¶
Solution 1: Use List comprehension¶
In [55]:
x = [0,1,3,5,8,7,6]
In [57]:
evens = [i for i in x if i%2 == 0]
evens
Out[57]:
[0, 8, 6]
Solution 2: Use filter
with functions¶
filter
function takes a list and a function returning bool as
argument and filter
’s the list, returns the iterator through
filter
ed list. One can use list()
to convert iterator to a
list
Usage
result = list(filter(condition,collection))
condition
is a boolean function that takes an element as input
In [58]:
def is_even(item):
return item % 2 == 0
In [61]:
list(filter(is_even,x))
Out[61]:
[0, 8, 6]
Solution 3: Use
s¶
In previous example, we passed a function object to filter()
. The
same case happens in many situations. In some cases function to be
passed might be too short like is_even()
. In this case lambda
s
can be used. lambda
s create function in place.
Usage:
function_name = lambda argument_list : executable_statements
This has the same effect as that of
def function_name(argument_list):
executable_statements
Now our is_even
function can be defined in terms of lambdas
is_even = lambda x : x % 2 == 0 # Note that return may be omitted
If multi-line statements are needed, Statements can be put inside
()
s or line can be extended with \
s
In [62]:
list(filter(lambda x: x % 2 == 0,x))
Out[62]:
[0, 8, 6]
Lambdas are a fundamental concept of Functional Programming where every task is achieved via a function. They constitute the basis of a branch of Mathematics and Computation Theory called :math:`lambda` calculus.
As a final thought, we shall see Recursive Fibonacci Generator in terms
of lambda
s
In [65]:
fibonacci_lambda = lambda n,first=0,second=1 :\
[] if n == 0 \
else \
[first] + fibonacci_recursive(n - 1, second, first + second)
In [66]:
fibonacci_lambda(4)
Out[66]:
[0, 1, 1, 2]
Note that lines are broken with \