Python Workshop

Saurabh Kumar/ [@thes_kumar]1

http://saurabh-kumar.com/pysmvdu

Table of Content

  • Getting Started
  • Working with data
  • Modules
  • Object Oriented Programming
  • Applications and popular frameworks

1. Getting Started

1.1. Running Python Interpreter

Python comes with an interactive interpreter. When you type python in your shell or command prompt, the python interpreter becomes active with a >>> prompt and waits for your commands.

$ python
Python 2.7.1 (r271:86832, Mar 17 2011, 07:02:35)
[GCC 4.2.1 (Apple Inc. build 5664)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>

Problem 1: Open a new Python interpreter and use it to find the value of 2+3.

In [2]:
454-90
Out[2]:
364

1.2. Running Python Scripts

Open your text editor, type the following text and save it as hello.py.

print "hello, world!"

And run this program by calling python hello.py. Make sure you change to the directory where you saved the file before doing it.

    theskumar@local ~$ python hello.py  
    hello, world!  
    theskumar@local ~$  

Problem 2: Create a python script to print hello, world! four times.

Problem 3: Create a python script with the following text and see the output.

1 + 2

If it doesn’t print anything, what changes can you make to the program to print the value?

1.3. Assignments

One of the building blocks of programming is associating a name to a value. This is called assignment. The associated name is usually called a variable.

In [20]:
x = 4
print x
4

In this example x is a variable and it’s value is 4.

If you try to use a name that is not associated with any value, python gives an error message.

>>> foo
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
NameError: name 'foo' is not defined

>>> foo = 4
>>> foo
4

If you re-assign a different value to an existing variable, the new value overwrites the old value.

>>> x = 4
>>> x
4
>>> x = 'hello'
>>> x
'hello'

It is possible to do multiple assignments at once.

>>> a, b = 1, 2
>>> a
1
>>> b
2
>>> a + b
3

Swapping values of 2 variables in python is very simple.

>>> a, b = 1, 2
>>> a, b = b, a
>>> a
2
>>> b
1

When executing assignments, python evaluates the right hand side first and then assigns those values to the variables specified in the left hand side.

Problem 4: What will be output of the following program.

In [3]:
x = 4
y = x + 1
x = 2
print x, y
2 5

Problem 5: What will be the output of the following program.

In [4]:
x, y = 2, 6
x, y = y, x + 2
print x, y
6 4

Problem 6: What will be the output of the following program.

In [5]:
a, b = 2, 3
b, c = b + 1, a 
print a, b, c
2 4 2

1.4. Numbers

We already know how to work with numbers.

In [5]:
4 + 2
Out[5]:
6
In [4]:
4.2
Out[4]:
4.2

Python also supports decimal numbers.

In [3]:
4.2 + 2.3
Out[3]:
6.5

Python supports the following operators on numbers.

  • + addition
  • - subtraction
  • * multiplication
  • / division
  • ** exponent
  • % remainder

Let’s try them on integers.

In [21]:
7 + 2
Out[21]:
9
In [22]:
7 - 2
Out[22]:
5
In [23]:
7 * 2
Out[23]:
14
In [24]:
7 / 2
Out[24]:
3
In [25]:
7 ** 2
Out[25]:
49
In [26]:
7 % 2
Out[26]:
1

If you notice, the result 7 / 2 is 3 not 3.5. It is because the / operator when working on integers, produces only an integer.

Lets see what happens when we try it with decimal numbers:

In [27]:
7.0 / 2.0
Out[27]:
3.5
In [28]:
7.0 / 2
Out[28]:
3.5
In [29]:
7 / 2.0
Out[29]:
3.5

The operators can be combined.

In [30]:
7 + 2 + 5 - 3
Out[30]:
11
In [31]:
2 * 3 + 4
Out[31]:
10

It is important to understand how these compound expressions are evaluated. The operators have precedence, a kind of priority that determines which operator is applied first.

1.5. Strings

Strings what you use to represent text.

Strings are a sequence of characters, enclosed in single quotes or double quotes.

In [6]:
x = "hello"
y = 'world'
print x, y
hello world

There is difference between single quotes and double quotes, they can used interchangebly.

Multi-line strings can be written using three single quotes or three double quotes.

In [7]:
x = """This is a multi-line string
written in
three lines."""
print x
This is a multi-line string
written in
three lines.

In [10]:
y = '''multi-line strings can be written
using three single quote characters as well.
The string can contain 'single quotes' or "double quotes"
in side it.'''
y
Out[10]:
'multi-line strings can be written\nusing three single quote characters as well.\nThe string can contain \'single quotes\' or "double quotes"\nin side it.'

1.6. Functions

Just like a value can be associated with a name, a piece of logic can also be associated with a name by defining a function.

In [12]:
def square(x):
    return x * x

square(5)
Out[12]:
25
In [13]:
# The functions can be used in any expressions.
square(2) + square(3)
Out[13]:
13

Existing functions can be used in creating new functions.

In [3]:
def sum_of_squares(x, y):
    return square(x) + square(y)

sum_of_squares(2, 3)
Out[3]:
13

Functions are just like other values, they can assigned, passed as arguments to other functions etc.

In [15]:
f = square
f(45)
Out[15]:
2025
In [16]:
def fxy(f, x, y):
    return f(x) + f(y)

fxy(square, 45, 3)
Out[16]:
2034

It is important to understand, the scope of the variables used in functions.

In [17]:
x = 0
y = 0
def incr(x):
    global y
    y = x + 1
    return y
incr(5)
print x, y
0 6

Variables assigned in a function, including the arguments are called the local variables to the function. The variables defined in the top-level are called global variables.

Changing the values of x and y inside the function incr won’t effect the values of global x and y.

In [8]:
# But, we can use the values of the global variables.

pi = 3.14
def area(r):
    return pi * r * r

Problem 7: How many multiplications are performed when each of the following lines of code is executed?

print square(5)
print square(2*5)

Problem 8: What will be the output of the following program?

In [20]:
x = 1
def f():
    return x
print x
print f()
1
1

Problem 9: What will be the output of the following program?

In [21]:
x = 1
def f():
    x = 2
    return x
print x
print f()
print x
1
2
1

Problem 10: What will be the output of the following program?

In [22]:
x = 1
def f():
        y = x
        x = 2
        return x + y
print x
print f()
print x
---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
<ipython-input-22-8bf8999f6206> in <module>()
      5         return x + y
      6 print x
----> 7 print f()
      8 print x

<ipython-input-22-8bf8999f6206> in f()
      1 x = 1
      2 def f():
----> 3         y = x
      4         x = 2
      5         return x + y

UnboundLocalError: local variable 'x' referenced before assignment
1

Problem 11: What will be the output of the following program?

In [23]:
x = 2
def f(a):
    x = a * a
    return x
y = f(3)
print x, y
2 9

Functions can be called with keyword arguments.

In [24]:
def difference(x, y):
    return x - y
In [25]:
difference(5, 2)
Out[25]:
3
In [26]:
difference(y=2, x=5)
Out[26]:
3
In [27]:
difference(5, y=2)
Out[27]:
3
In [28]:
difference(y=2, x=5)
Out[28]:
3

And some arguments can have default values.

In [30]:
def increment(x, amount=1):
    return x + amount
In [31]:
increment(10)
Out[31]:
11
In [32]:
increment(10, 5)
Out[32]:
15
In [33]:
increment(10, amount=2)
Out[33]:
12

There is another way of creating functions, using the lambda operator.

In [34]:
cube = lambda x: x ** 3
In [35]:
fxy(cube, 2, 3)
Out[35]:
35
In [36]:
fxy(lambda x: x ** 3, 2, 3)
Out[36]:
35

1.6.1. Built-in Functions

Python provides some useful built-in functions.

In [37]:
min(2, 3)
Out[37]:
2
In [38]:
max(3, 4)
Out[38]:
4

The built-in function len computes length of a string.

In [39]:
len("helloworld")
Out[39]:
10

The built-in function int converts string to ingeter and built-in function str converts integers and other type of objects to strings.

In [41]:
int("50")
Out[41]:
50
In [40]:
str(123)
Out[40]:
'123'

Problem 12: Write a function count_digits to find number of digits in the given number.

>>> count_digits(5)
1
>>> count_digits(12345)
5

1.6.2. Methods

Methods are special kind of functions that work on an object.

For example, upper is a method available on string objects.

In []:
x = "hello"
print x.upper()

As already mentioned, methods are also functions. They can be assigned to other variables can be called separately.

In []:
f = x.upper
print f()

Problem 13: Write a function istrcmp to compare two strings, ignoring the case.

>>> istrcmp('python', 'Python')
True
>>> istrcmp('LaTeX', 'Latex')
True
>>> istrcmp('a', 'b')
False

1.7. Conditional Expressions

Python provides various operators for comparing values. The result of a comparison is a boolean value, either True or False.

In []:
2 < 3
In []:
2 > 3

Here is the list of available conditional operators.

  • == equal to
  • != not equal to
  • < less than
  • > greater than
  • <= less than or equal to
  • >= greater than or equal to

It is even possible to combine these operators.

In [42]:
x = 5
2 < x < 10
Out[42]:
True
In [43]:
2 < 3 < 4 < 5 < 6
Out[43]:
True

The conditional operators work even on strings - the ordering being the lexical order.

In [44]:
"python" > "perl"
Out[44]:
True
In [45]:
"python" > "java"
Out[45]:
True

There are few logical operators to combine boolean values.

  • a and b is True only if both a and b are True.
  • a or b is True if either a or b is True.
  • not a is True only if a is False.
In [46]:
True and True
Out[46]:
True
In [47]:
True and False
Out[47]:
False
In [48]:
2 < 3 and 5 < 4
Out[48]:
False
In [49]:
2 < 3 or 5 < 4
Out[49]:
True

Problem 14: What will be output of the following program?

In [50]:
print 2 < 3 and 3 > 1
True

In [51]:
print 2 < 3 or 3 > 1
True

In [52]:
print 2 < 3 or not 3 > 1
True

In [53]:
print 2 < 3 or not 3 > 1
True

Problem 15: What will be output of the following program?

In [54]:
x = 4
y = 5
z = 1
p = x < y or x < z
print p
True

Problem 16: What will be output of the following program?

In [55]:
True, False = False, True
print True, False
print 2 < 3
False True
True

1.7.1. The if statement

The `if` statement is used to execute a piece of code only when a boolean expression is true.
In []:
x = 42
if x % 2 == 0: print 'even'

In this example, print 'even' is executed only when x % 2 == 0 is True.

The code associated with if can be written as a separate indented block of code, which is often the case when there is more than one statement to be executed.

In []:
if x % 2 == 0:
    print 'even'

The if statement can have optional else clause, which is executed when the boolean expression is False.

In []:
x = 3
if x % 2 == 0:
    print 'even'
else:
    print 'odd'

The if statement can have optional elif clauses when there are more conditions to be checked. The elif keyword is short for else if, and is useful to avoid excessive indentation.

In []:
x = 42
if x < 10:
       print 'one digit number'
elif x < 100:
    print 'two digit number'
else:
    print 'big number'

Problem 17: What happens when the following code is executed? Will it give any error? Explain the reasons.

In []:
x = 2
if x == 2:
    print x
else:
    print y

Problem 18: What happens when the following code is executed? Will it give any error? Explain the reasons.

In []:
x = 2
if x == 2:
    print x
else:
    x +

1.8. Lists

Lists are one of the great datastructures in Python. We are going to learn a little bit about lists now. Basic knowledge of lists is requrired to be able to solve some problems that we want to solve in this section.

Here is a list of numbers.

>>> x = [1, 2, 3]

And here is a list of strings.

>>> x = ["hello", "world"]

List can be heterogeneous. Here is a list containings integers, strings and another list.

>>> x = [1, 2, "hello, "world", ["another", "list"]]

The built-in function len works for lists as well.

In [56]:
x = [1, 2, 3]
len(x)
Out[56]:
3

The [] operator is used to access individual elements of a list.

In [57]:
x = [1, 2, 3]
x[1]
Out[57]:
2
In [58]:
x[1] = 4
x[1]
Out[58]:
4

The first element is indexed with 0, second with 1 and so on.

1.9. Modules

Modules are libraries in Python. Python ships with many standard library modules.

A module can be imported using the import statement.

Lets look at time module for example:

In [59]:
import time
time.asctime()
Out[59]:
'Sat Oct 26 17:27:11 2013'

The asctime function from the time module returns the current time of the system as a string.

The sys module provides access to the list of arguments passed to the program, among the other things.

The sys.argv variable contains the list of arguments passed to the program. As a convention, the first element of that list is the name of the program.

Lets look at the following program echo.py that prints the first argument passed to it.

import sys
print sys.argv[1]

Lets try running it.

$ python echo.py hello
hello
$ python echo.py hello world
hello

Problem 19: Write a program add.py that takes 2 numbers as command line arguments and prints its sum.

$ python add.py 3 5
8
$ python add.py 2 9
11

2. Working with Data

2.1. Lists

We’ve already seen quick introduction to lists in the previous section.

>>> [1, 2, 3, 4]
[1, 2, 3, 4]
>>> ["hello", "world"]
["hello", "world"]
>>> [0, 1.5, "hello"]
[0, 1.5, "hello"]
>>> [0, 1.5, "hello"]
[0, 1.5, "hello"]

A List can contain another list as member.

In [60]:
a = [1, 2]
b = [1.5, 2, a]
b
Out[60]:
[1.5, 2, [1, 2]]

The built-in function range can be used to create a list of integers.

In [61]:
range(4)
Out[61]:
[0, 1, 2, 3]
In [62]:
range(3, 6)
Out[62]:
[3, 4, 5]
In [63]:
range(2, 10, 3)
Out[63]:
[2, 5, 8]

The built-in function len can be used to find the length of a list.

In [64]:
a = [1, 2, 3, 4]
len(a)
Out[64]:
4

The + and * operators work even on lists.

In [65]:
a = [1, 2, 3]
b = [4, 5]
In [66]:
a + b
Out[66]:
[1, 2, 3, 4, 5]
In [67]:
b * 3
Out[67]:
[4, 5, 4, 5, 4, 5]

List can be indexed to get individual entries. Value of index can go from 0 to (length of list - 1).

In [68]:
x = [1, 2]
In [69]:
x[0]
Out[69]:
1
In [70]:
x[1]
Out[70]:
2

When a wrong index is used, python gives an error.

In [12]:
x = [1, 2, 3, 4]
x[6]
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-12-478242a9f961> in <module>()
      1 x = [1, 2, 3, 4]
----> 2 x[6]

IndexError: list index out of range

Negative indices can be used to index the list from right.

In [71]:
x = [1, 2, 3, 4]
In [72]:
x [-1]
Out[72]:
4
In [73]:
x[-2]
Out[73]:
3

We can use list slicing to get part of a list.

In [16]:
x = [1, 2, 3, 4]
In [15]:
x[0:2]
Out[15]:
[1, 2]
In [74]:
x[1:4]
Out[74]:
[2, 3, 4]

Even negative indices can be used in slicing. For example, the following examples strips the last element from the list.

In [13]:
x[0:-1]
Out[13]:
[1, 2, 3]

Slice indices have useful defaults; an omitted first index defaults to zero, an omitted second index defaults to the size of the list being sliced.

In [75]:
x = [1, 2, 3, 4]
In [76]:
a[:2]  # first index defaults to zero
Out[76]:
[1, 2]
In [77]:
a[2:]  # last index defaults to lenght of list
Out[77]:
[3]
In []:
a[:]  # all of 'em

An optional third index can be used to specify the increment, which defaults to 1.

In [78]:
x = range(10)
x
Out[78]:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In [18]:
x[0:6:2]
Out[18]:
[0, 2, 4]

We can reverse a list, just by providing -1 for increment.

In [80]:
x[::-2]
Out[80]:
[9, 7, 5, 3, 1]

List members can be modified by assignment.

In [82]:
x = [1, 2, 3, 4]
x[1] = 5
x
Out[82]:
[1, 5, 3, 4]

Presence of a key in a list can be tested using in operator.

In [83]:
x = [1, 2, 3, 4]
In [84]:
2 in x
Out[84]:
True
In [85]:
10 in x
Out[85]:
False

Values can be appended to a list by calling append method on list. A method is just like a function, but it is associated with an object and can access that object when it is called. We will learn more about methods when we study classes.

In [24]:
a = [1, 2]
a.append(3)
a
Out[24]:
[1, 2, 3]

Problem 1: What will be the output of the following program?

In []:
x = [0, 1, [2]]
x[2][0] = 3
print x
x[2].append(4)
print x
x[2] = 2
print x

2.1.1. The for Statement

Python provides for statement to iterate over a list. A for statement executes the specified block of code for every element in a list.

In [25]:
for x in [1, 2, 3, 4]:
    print x
1
2
3
4

In [26]:
for i  in range(10):
   print i, i*i, i*i*i
0 0 0
1 1 1
2 4 8
3 9 27
4 16 64
5 25 125
6 36 216
7 49 343
8 64 512
9 81 729

The built-in function zip takes two lists and returns list of pairs.

In [27]:
zip(["a", "b", "c"], [1, 2, 3])
Out[27]:
[('a', 1), ('b', 2), ('c', 3)]

It is handy when we want to iterate over two lists together.

In [28]:
names = ["a", "b", "c"]
values = [1, 2, 3]
for name, value in zip(names, values):
    print name, value
a 1
b 2
c 3

Problem 2: Python has a built-in function sum to find sum of all elements of a list. Provide an implementation for sum.

>>> sum([1, 2, 3])
>>> 4

Problem 3: What happens when the above sum function is called with a list of strings? Can you make your sum function work for a list of strings as well.

>>> sum(["hello", "world"])
"helloworld"
>>> sum(["aa", "bb", "cc"])
"aabbcc"

Problem 4: Implement a function product, to compute product of a list of numbers.

>>> product([1, 2, 3])
6

Problem 5: Write a function factorial to compute factorial of a number. Can you use the product function defined in the previous example to compute factorial?

>>> factorial(4)
24

Problem 6: Write a function reverse to reverse a list. Can you do this without using list slicing?

>>> reverse([1, 2, 3, 4])
[4, 3, 2, 1]
>>> reverse(reverse([1, 2, 3, 4]))
[1, 2, 3, 4]

Problem 7: Python has built-in functions min and max to compute minimum and maximum of a given list. Provide an implementation for these functions. What happens when you call your min and max functions with a list of strings?

2.1.2. Sorting Lists

The sort method sorts a list in place.

In []:
a = [2, 10, 4, 3, 7]
a.sort()
a

The built-in function sorted returns a new sorted list without modifying the source list.

In [2]:
a = [4, 3, 5, 9, 2]
sorted(a)
a
Out[2]:
[4, 3, 5, 9, 2]

The sort method works even when the list has different types of objects and even lists.

In [30]:
a = ["hello", 1, "world", 45, 2]
a.sort()
a
Out[30]:
[1, 2, 45, 'hello', 'world']
In [29]:
a = [[2, 3], [1, 6]]
a.sort()
a
Out[29]:
[[1, 6], [2, 3]]

We can optionally specify a function as sort key.

In [1]:
a = [[2, 3], [4, 6], [6, 1]]
a.sort(key=lambda x: x[1])
a
Out[1]:
[[6, 1], [2, 3], [4, 6]]

This sorts all the elements of the list based on the value of second element of each entry.

Problem 13: Write a function lensort to sort a list of strings based on length.

>>> lensort(['python', 'perl', 'java', 'c', 'haskell', 'ruby'])

2.2. Tuples

Tuple is a sequence type just like list, but it is immutable. A tuple consists of a number of values separated by commas.

In [31]:
a = (1, 2, 3)
a[0]
Out[31]:
1
In [33]:
len(a)
Out[33]:
3
In [34]:
a[1:]
Out[34]:
(2, 3)

The enclosing braces are optional.

In [32]:
a = 1, 2, 3
a[0]
1
Out[32]:
1

Since parenthesis are also used for grouping, tuples with a single value are represented with an additional comma.

In [36]:
a = (1)
a
Out[36]:
1
In [35]:
b = (1,)
b
Out[35]:
(1,)
In [37]:
b[0]
Out[37]:
1

2.3. Sets

Sets are unordered collection of unique elements.

In [39]:
x = set([3, 1, 2, 1])
x
Out[39]:
{1, 2, 3}
In [40]:
x = {3, 1, 2, 1}  # new in python 2.7
x
Out[40]:
{1, 2, 3}

New elements can be added to a set using the add method.

In [41]:
x = set([1, 2, 3])
x.add(4)
x
Out[41]:
{1, 2, 3, 4}

Just like lists, the existance of an element can be checked using the in operator. However, this operation is faster in sets compared to lists.

In [43]:
x = set([1, 2, 3])
In [44]:
1 in x
Out[44]:
True
In [45]:
5 in x
Out[45]:
False

2.4. Strings

Strings also behave like lists in many ways. Length of a string can be found using built-in function len.

In [46]:
len("abrakadabra")
Out[46]:
11

Indexing and slicing on strings behave similar to that of lists.

In [47]:
a = "helloworld"
In [48]:
a[1]
Out[48]:
'e'
In [49]:
a[-2]
Out[49]:
'l'
In [50]:
a[1:5]
Out[50]:
'ello'
In [51]:
a[:5]
Out[51]:
'hello'
In [52]:
a[5:]
Out[52]:
'world'
In [53]:
a[:-2]
Out[53]:
'hellowor'
In [54]:
a[::-1]  # reverse!
Out[54]:
'dlrowolleh'

The in operator can be used to check if a string is present in another string.

In [55]:
'hell' in 'hello'
Out[55]:
True
In [56]:
'full' in 'hello'
Out[56]:
False
In [57]:
'el' in 'hello'
Out[57]:
True

There are many useful methods on strings.

The split method splits a string using a delimiter. If no delimiter is specified, it uses any whitespace char as delimiter.

In [58]:
"hello world".split()
Out[58]:
['hello', 'world']
In [59]:
"a,b,c".split(',')
Out[59]:
['a', 'b', 'c']

The join method joins a list of strings.

In [60]:
" ".join(['hello', 'world'])
Out[60]:
'hello world'
In [61]:
','.join(['a', 'b', 'c'])
Out[61]:
'a,b,c'

Problem 14: Write a function extsort to sort a list of files based on extension.

>>> extsort(['a.c', 'a.py', 'b.py', 'bar.txt', 'foo.txt', 'x.c'])
['a.c', 'x.c', 'a.py', 'b.py', 'bar.txt', 'foo.txt']

2.6. List Comprehensions

List Comprehensions provide a concise way of creating lists. Many times a complex task can be modelled in a single line.

Here are some simple examples for transforming a list.

In [62]:
a = range(10)
a
Out[62]:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In [63]:
[x for x in a]
Out[63]:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In [64]:
[x*x for x in a]
Out[64]:
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
In [65]:
[x+1 for x in a]
Out[65]:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

It is also possible to filter a list using if inside a list comprehension.

In [66]:
a = range(10)
In [67]:
[x for x in a if x % 2 == 0]
Out[67]:
[0, 2, 4, 6, 8]
In [68]:
[x*x for x in a if x%2 == 0]
Out[68]:
[0, 4, 16, 36, 64]

It is possible to iterate over multiple lists using the built-in function zip.

In [69]:
a = [1, 2, 3, 4]
b = [2, 3, 5, 7]
zip(a, b)
Out[69]:
[(1, 2), (2, 3), (3, 5), (4, 7)]
In [70]:
[x+y for x, y in zip(a, b)]
Out[70]:
[3, 5, 8, 11]

we can use multiple for clauses in single list comprehension.

In [71]:
[(x, y) for x in range(5) for y in range(5) if (x+y)%2 == 0]
Out[71]:
[(0, 0),
 (0, 2),
 (0, 4),
 (1, 1),
 (1, 3),
 (2, 0),
 (2, 2),
 (2, 4),
 (3, 1),
 (3, 3),
 (4, 0),
 (4, 2),
 (4, 4)]
In [72]:
[(x, y) for x in range(5) for y in range(5) if (x+y)%2 == 0 and x != y]
Out[72]:
[(0, 2), (0, 4), (1, 3), (2, 0), (2, 4), (3, 1), (4, 0), (4, 2)]
In [73]:
[(x, y) for x in range(5) for y in range(x) if (x+y)%2 == 0]
Out[73]:
[(2, 0), (3, 1), (4, 0), (4, 2)]

The following example finds all Pythagorean triplets using numbers below 25. (x, y, z) is a called pythagorean triplet if x*x + y*y == z*z.

In [74]:
n = 25
[(x, y, z) for x in range(1, n) for y in range(x, n) for z in range(y, n) if x*x + y*y == z*z]
Out[74]:
[(3, 4, 5), (5, 12, 13), (6, 8, 10), (8, 15, 17), (9, 12, 15), (12, 16, 20)]

Problem 24: Provide an implementation for zip function using list comprehensions.

>>> zip([1, 2, 3], ["a", "b", "c"])
[(1, "a"), (2, "b"), (3, "c")]

Problem 25: Python provides a built-in function map that applies a function to each element of a list. Provide an implementation for map using list comprehensions.

>>> def square(x): return x * x
...
>>> map(square, range(5))
[0, 1, 4, 9, 16]

2.7. Dictionaries

Dictionaries are like lists, but they can be indexed with non integer keys also. Unlike lists, dictionaries are not ordered.

In [76]:
a = {'x': 1, 'y': 2, 'z': 3}
In [77]:
a['x']
Out[77]:
1
In [78]:
a['z']
Out[78]:
3
In [83]:
b = {}
In [84]:
b['x'] = 2
In [85]:
b[2] = 'foo'
In [86]:
b[(1, 2)] = 3

The del keyword can be used to delete an item from a dictionary.

In [87]:
a = {'x': 1, 'y': 2, 'z': 3}
del a['x']
a
Out[87]:
{'y': 2, 'z': 3}
In []:
del a['x']

The keys method returns all keys in a dictionary, the values method returns all values in a dictionary and items method returns all key-value pairs in a dictionary.

In [88]:
a.keys()
Out[88]:
['y', 'z']
In [89]:
a.values()
Out[89]:
[2, 3]
In [90]:
a.items()
Out[90]:
[('y', 2), ('z', 3)]

The for statement can be used to iterate over a dictionary.

In [91]:
for key in a: print key
y
z

In [92]:
for key, value in a.items(): print key, value
y 2
z 3

Presence of a key in a dictionary can be tested using in operator or has_key method.

In [93]:
a = {'x': 1, 'y': 2, 'z': 3}
In [94]:
'x' in a
Out[94]:
True
In [95]:
'p' in a
Out[95]:
False
In [96]:
a.has_key('x')
Out[96]:
True
In [97]:
a.has_key('p')
Out[97]:
False

3. Modules

Modules are reusable libraries of code in Python. Python comes with many standard library modules.

A module is imported using the import statement.

In [11]:
import time
print time.asctime()
Sat Oct 26 06:58:07 2013

In this example, we’ve imported the time module and called the asctime function from that module, which returns current time as a string.

There is also another way to use the import statement.

In [12]:
from time import asctime
asctime()
Out[12]:
'Sat Oct 26 06:59:42 2013'

Here were imported just the asctime function from the time module.

The pydoc command provides help on any module or a function.

$ pydoc time
Help on module time:

NAME
    time - This module provides various functions to manipulate time values.
...

$ pydoc time.asctime
Help on built-in function asctime in time:

time.asctime = asctime(...)
    asctime([tuple]) -> string
...

On Windows, the pydoc command is not available. The work-around is to use, the built-in help function.

>>> help('time')
Help on module time:

NAME
    time - This module provides various functions to manipulate time values.
...

Writing our own modules is very simple.

For example, create a file called num.py with the following content.

In [13]:
def square(x):
    return x * x

def cube(x):
    return x * x * x

Now open Python interterter:

>>> import num
>>> num.square(3)
9
>>> num.cube(3)
27

Thats all we’ve written a python library.

Try pydoc num (pydoc.bat numbers on Windows) to see documentation for this numbers modules. It won’t have any documentation as we haven’t providied anything yet.

In Python, it is possible to associate documentation for each module, function using docstrings. Docstrings are strings written at the top of the module or at the beginning of a function.

Lets try to document our num module by changing the contents of num.py

In []:
"""The num module provides utilties to work on numbers.

Current it provides square and cube.
"""

def square(x):
    """Computes square of a number."""
    return x * x

def cube(x):
    """Computes cube of a number."""
    return x * x

The pydoc command will now show us the doumentation nicely formatted.

Help on module num:

NAME
    num - The num module provides utilties to work on numbers.

FILE
    /Users/theskumar/num.py

DESCRIPTION
    Current it provides square and cube.

FUNCTIONS
    cube(x)
        Computes cube of a number.

    square(x)
        Computes square of a number.

Under the hood, python stores the documentation as a special field called __doc__.

In [17]:
import os
print os.getcwd.__doc__
getcwd() -> path

Return a string representing the current working directory.

3.1. Standard Library

Python comes with many standard library modules. Lets look at some of the most commonly used ones.

3.1.1. os module

The os and os.path modules provides functionality to work with files, directories etc.

Problem 1: Write a program to list all files in the given directory.

Problem 2: Write a program extcount.py to count number of files for each extension in the given directory. The program should take a directory name as argument and print count and extension for each available file extension.

$ python extcount.py src/
14 py
4 txt
1 csv

Problem 3: Write a program to list all the files in the given directory along with their length and last modification time. The output should contain one line for each file containing filename, length and modification date separated by tabs.

Hint: see help for os.stat.

Problem 4: Write a program to print directory tree. The program should take path of a directory as argument and print all the files in it recursively as a tree.

$ python dirtree.py foo
foo
|-- a.txt
|-- b.txt
|-- code
|   |-- a.py
|   |-- b.py
|   |-- docs
|   |   |-- a.txt
|   |   \-- b.txt
|   \-- x.py
\-- z.txt
In []:
### 3.1.2. urllib module¶

The urllib module provides functionality to download webpages.

    >>> import urllib
    >>> response = urllib.urlopen("http://python.org/")
    >>> print response.headers
    Date: Fri, 30 Mar 2013 09:24:55 GMT
    Server: Apache/2.2.16 (Debian)
    Last-Modified: Fri, 30 Mar 2013 08:42:25 GMT
    ETag: "105800d-4b7b-4bc71d1db9e40"
    Accept-Ranges: bytes
    Content-Length: 19323
    Connection: close
    Content-Type: text/html
    X-Pad: avoid browser bug
    
    >>> response.header['Content-Type']
    'text/html'
    
    >>> content = request.read()

3.1.4. json module

Problem 10: Write a program myip.py to print the external IP address of the machine. Use the response from http://httpbin.org/get and read the IP address from there. The program should print only the IP address and nothing else.

3.1.5. zipfie module

The zipfile module provides interface to read and write zip files.

Here are some examples to demonstate the power of zipfile module.

The following example prints names of all the files in a zip archive.

import zipfile
z = zipfile.ZipFile("a.zip")
for name in z.namelist():
    print name

The following example prints each file in the zip archive.

import zipfile
z = zipfile.ZipFile("a.zip")
for name in z.namelist():
    print
    print "FILE:", name
    print
    print z.read(name)

Problem 11: Write a python program zip.py to create a zip file. The program should take name of zip file as first argument and files to add as rest of the arguments.

Problem 12: Write a program mydoc.py to implement the functionality of pydoc. The program should take the module name as argument and print documentation for the module and each of the functions defined in that module.

$ python mydoc.py os

Help on module os:

DESCRIPTION

os - OS routines for Mac, NT, or Posix depending on what system we're on.
...

FUNCTIONS

getcwd()
    ...

Hints: The dir function to get all entries of a module The inspect.isfunction function can be used to test if given object is a function x.doc gives the docstring for x. The import function can be used to import a module by name

In []:
\

4. Object Oriented Programming¶

4.1. State

Suppose we want to model a bank account with support for deposit and withdraw operations. One way to do that is by using global state as shown in the following example.

balance = 0

def deposit(amount):
    global balance
    balance += amount
    return balance

def withdraw(amount):
    global balance
    balance -= amount
    return balance

The above example is good enough only if we want to have just a single account. Things start getting complicated if want to model multiple accounts.

We can solve the problem by making the state local, probably by using a dictionary to store the state.

In [1]:
def make_account():
    return {'balance': 0}

def deposit(account, amount):
    account['balance'] += amount
    return account['balance']

def withdraw(account, amount):
    account['balance'] -= amount
    return account['balance']

With this it is possible to work with multiple accounts at the same time.

In [2]:
a = make_account()
b = make_account()
deposit(a, 100)
Out[2]:
100
In [3]:
deposit(b, 50)
Out[3]:
50
In [4]:
withdraw(b, 10)
Out[4]:
40
In [5]:
withdraw(a, 10)
Out[5]:
90

4.2. Classes and Objects

In [6]:
class BankAccount:
    def __init__(self):
        self.balance = 0

    def withdraw(self, amount):
        self.balance -= amount
        return self.balance

    def deposit(self, amount):
        self.balance += amount
        return self.balance
In [9]:
a = BankAccount()
b = BankAccount()
In [10]:
a.deposit(100)
Out[10]:
100
In [11]:
b.deposit(50)
Out[11]:
50
In [12]:
b.withdraw(10)
Out[12]:
40
In [13]:
a.withdraw(10)
Out[13]:
90

4.3. Inheritance¶

Let us try to create a little more sophisticated account type where the account holder has to maintain a pre-determined minimum balance.

class MinimumBalanceAccount(BankAccount):
    def __init__(self, minimum_balance):
        BankAccount.__init__(self)
        self.minimum_balance = minimum_balance

    def withdraw(self, amount):
        if self.balance - amount < self.minimum_balance:
            print 'Sorry, minimum balance must be maintained.'
        else:
            BankAccount.withdraw(self, amount)

Problem 1: What will the output of the following program.

In []:
class A:
    def f(self):
        return self.g()

    def g(self):
        return 'A'

class B(A):
    def g(self):
        return 'B'

a = A()
b = B()
print a.f(), b.f()
print a.g(), b.g()

Example: Drawing Shapes

In []:
class Canvas:
    def __init__(self, width, height):
        self.width = width
        self.height = height
        self.data = [[' '] * width for i in range(height)]

    def setpixel(self, row, col):
        self.data[row][col] = '*'

    def getpixel(self, row, col):
        return self.data[row][col]

    def display(self):
        print "\n".join(["".join(row) for row in self.data])

class Shape:
    def paint(self, canvas): pass

class Rectangle(Shape):
    def __init__(self, x, y, w, h):
        self.x = x
        self.y = y
        self.w = w
        self.h = h

    def hline(self, x, y, w):
        pass

    def vline(self, x, y, h):
        pass

    def paint(self, canvas):
        hline(self.x, self.y, self.w)
        hline(self.x, self.y + self.h, self.w)
        vline(self.x, self.y, self.h)
        vline(self.x + self.w, self.y, self.h)

class Square(Rectangle):
    def __init__(self, x, y, size):
        Rectangle.__init__(self, x, y, size, size)

class CompoundShape(Shape):
    def __init__(self, shapes):
        self.shapes = shapes

    def paint(self, canvas):
        for s in self.shapes:
            s.paint(canvas)

4.4. Special Class Methods

In Python, a class can implement certain operations that are invoked by special syntax (such as arithmetic operations or subscripting and slicing) by defining methods with special names. This is Python’s approach to operator overloading, allowing classes to define their own behavior with respect to language operators.

For example, the + operator invokes add method.

In [1]:
a, b = 1, 2
a + b
Out[1]:
3
In [2]:
a.__add__(b)
Out[2]:
3

Just like __add__ is called for + operator, __sub__, __mul__ and __div__ methods are called for -, *, and / operators.

Example: Rational Numbers

Suppose we want to do arithmetic with rational numbers. We want to be able to add, subtract, multiply, and divide them and to test whether two rational numbers are equal.

We can add, subtract, multiply, divide, and test equality by using the following relations:

n1/d1 + n2/d2 = (n1*d2 + n2*d1)/(d1*d2)
n1/d1 - n2/d2 = (n1*d2 - n2*d1)/(d1*d2)
n1/d1 * n2/d2 = (n1*n2)/(d1*d2)
(n1/d1) / (n2/d2) = (n1*d2)/(d1*n2)

n1/d1 == n2/d2 if and only if n1*d2 == n2*d1

Lets write the rational number class.

In []:
class RationalNumber:
    """
    Rational Numbers with support for arthmetic operations.

        >>> a = RationalNumber(1, 2)
        >>> b = RationalNumber(1, 3)
        >>> a + b
        5/6
        >>> a - b
        1/6
        >>> a * b
        1/6
        >>> a/b
        3/2
    """
    def __init__(self, numerator, denominator=1):
        self.n = numerator
        self.d = denominator

    def __add__(self, other):
        if not isinstance(other, RationalNumber):
            other = RationalNumber(other)

        n = self.n * other.d + self.d * other.n
        d = self.d * other.d
        return RationalNumber(n, d)

    def __sub__(self, other):
        if not isinstance(other, RationalNumber):
            other = RationalNumber(other)

        n1, d1 = self.n, self.d
        n2, d2 = other.n, other.d
        return RationalNumber(n1*d2 - n2*d1, d1*d2)

    def __mul__(self, other):
        if not isinstance(other, RationalNumber):
            other = RationalNumber(other)

        n1, d1 = self.n, self.d
        n2, d2 = other.n, other.d
        return RationalNumber(n1*n2, d1*d2)

    def __div__(self, other):
        if not isinstance(other, RationalNumber):
            other = RationalNumber(other)

        n1, d1 = self.n, self.d
        n2, d2 = other.n, other.d
        return RationalNumber(n1*d2, d1*n2)

    def __str__(self):
        return "%s/%s" % (self.n, self.d)

    __repr__ = __str__

4.5. Errors and Exceptions

In []:
Weve already seen exceptions in various places. Python gives NameError when we try to use a variable that is not defined.
In [3]:
foo
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-3-d3b07384d113> in <module>()
----> 1 foo

NameError: name 'foo' is not defined
In [4]:
"foo" + 2
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-4-70e6053a6147> in <module>()
----> 1 "foo" + 2

TypeError: cannot concatenate 'str' and 'int' objects
In [5]:
2/0
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-5-897f73788bb0> in <module>()
----> 1 2/0

ZeroDivisionError: integer division or modulo by zero
In [6]:
open("not-there.txt")
---------------------------------------------------------------------------
IOError                                   Traceback (most recent call last)
<ipython-input-6-d4991abae57c> in <module>()
----> 1 open("not-there.txt")

IOError: [Errno 2] No such file or directory: 'not-there.txt'

Exceptions are handled by using the try-except statements.

def main():
    filename = sys.argv[1]
    try:
        for row in parse_csv(filename):
            print row
    except IOError:
        print >> sys.stderr, "The given file doesn't exist: ", filename
        sys.exit(1)
        

This above example prints an error message and exits with an error status when an IOError is encountered.

The except statement can be written in multiple ways:

# catch all exceptions
try:
    ...
except:

# catch just one exception
try:
    ...
except IOError:
    ...

# catch one exception, but provide the exception object
try:
    ...
except IOError, e:
    ...

# catch more than one exception
try:
    ...
except (IOError, ValueError), e:
    ...

It is possible to have more than one except statements with one try.

try: ... except IOError, e: print >> sys.stderr, "Unable to open the file (%s): %s" % (str(e), filename) sys.exit(1) except FormatError, e: print >> sys.stderr, "File is badly formatted (%s): %s" % (str(e), filename)

The try statement can have an optional else clause, which is executed only if no exception is raised in the try-block.

try:
    ...
except IOError, e:
    print >> sys.stderr, "Unable to open the file (%s): %s" % (str(e), filename)
    sys.exit(1)
else:
    print "successfully opened the file", filename

There can be an optional else clause with a try statement, which is executed irrespective of whether or not exception has occured.

try:
    ...
except IOError, e:
    print >> sys.stderr, "Unable to open the file (%s): %s" % (str(e), filename)
    sys.exit(1)
finally:
    delete_temp_files()
Exception is raised using the raised keyword.

raise Exception("error message")
All the exceptions are extended from the built-in Exception class.

class ParseError(Exception):
pass

Problem 2: What will be the output of the following program?

try:
    print "a"
except:
    print "b"
else:
    print "c"
finally:
    print "d"
Problem 3: What will be the output of the following program?

try:
    print "a"
    raise Exception("doom")
except:
    print "b"
else:
    print "c"
finally:
    print "d"
Problem 4: What will be the output of the following program?

def f():
    try:
        print "a"
        return
    except:
        print "b"
    else:
        print "c"
    finally:
        print "d"

f()