# 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 like in above code

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 encoded text, which has to be decoded 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.
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() joins the iterable with 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.
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 lambdas¶

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. filtering 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 filtered 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 lambdas can be used. lambdas 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 lambdas

In [5]:

fibonacci_lambda = \
lambda n,first=0,second=1 :\
[] if n == 0 \
else \
[first] + fibonacci_lambda(n - 1, second, first + second)

In [6]:

fibonacci_lambda(4)

Out[6]:

[0, 1, 1, 2]


Note that lines are broken with \