Python Training at Symantec - Chennai -- Day 2

March 27-31, 2017
Anand Chitipothu

These notes are available online at https://notes.pipal.in/2017/symantec

© Pipal Academy LLP

Home | Day 1 | Day 2 | Day 3 | Day 4 | Day 5

Topics for today

  • Modules
  • Reading Command-Line Arguments
  • Conditional Expressions
  • The if Statement
  • Lists
    • The for loop
    • Modifying and Growing Lists
    • List Comprehensions
    • Iteration Patterns
    • List Indexing and Slicing

Modules

In [1]:
import time
In [2]:
time.asctime()
Out[2]:
'Tue Mar 28 15:07:31 2017'
In [3]:
from time import asctime
In [4]:
asctime()
Out[4]:
'Tue Mar 28 15:07:52 2017'

Q: Where is the time module located? How will Python finds it?

In [9]:
import sys
In [10]:
sys.path
Out[10]:
['',
 '/Users/anand/github/dabeaz/curio',
 '/Users/anand/pyenvs/python35/lib/python35.zip',
 '/Users/anand/pyenvs/python35/lib/python3.5',
 '/Users/anand/pyenvs/python35/lib/python3.5/plat-darwin',
 '/Users/anand/pyenvs/python35/lib/python3.5/lib-dynload',
 '/usr/local/Cellar/python3/3.5.2_1/Frameworks/Python.framework/Versions/3.5/lib/python3.5',
 '/usr/local/Cellar/python3/3.5.2_1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/plat-darwin',
 '/Users/anand/pyenvs/python35/lib/python3.5/site-packages',
 '/Users/anand/pyenvs/python35/lib/python3.5/site-packages/IPython/extensions',
 '/Users/anand/.ipython']

Let us try with os module.

In [11]:
import os
In [12]:
os.__file__
Out[12]:
'/Users/anand/pyenvs/python35/bin/../lib/python3.5/os.py'

The __file__ is a special attribute available on modules, which provides the path to the module.

Now let us write a small program that uses the time module.

In [13]:
%%file date.py
import time
print(time.asctime())
Writing date.py
In [14]:
!python date.py
Tue Mar 28 15:12:26 2017

Let us now look at couple of standard library modules.

The os module

In [15]:
import os
In [16]:
# get the current working directory
os.getcwd() 
Out[16]:
'/Users/anand/trainings/2017/symantec'
In [17]:
# all files in the current directory
os.listdir(".")
Out[17]:
['.ipynb_checkpoints',
 'Makefile',
 'Readme.txt',
 'Welcome.html',
 'Welcome.ipynb',
 'date.py',
 'day1.html',
 'day1.ipynb',
 'day2.html',
 'day2.ipynb',
 'day3.html',
 'day3.ipynb',
 'day4.html',
 'day4.ipynb',
 'day5.html',
 'day5.ipynb',
 'hello.py',
 'index.html',
 'index.ipynb',
 'jhub',
 'notes.txt',
 'push']

Problem: Write a function count_files that takes path to a directory as argument and returns the number of files it has.

>>> count_files(".")
16
>>> count_files("/tmp")
9
In [18]:
def count_files(path):
    files = os.listdir(path)
    return len(files)
In [19]:
count_files(".")
Out[19]:
22
In [20]:
count_files("/tmp")
Out[20]:
18
In [21]:
# you can even write the function body in a single line
def count_files(path):
    return len(os.listdir(path))
In [22]:
count_files(".")
Out[22]:
22

Reading Command-line Arguments

In [23]:
%%file args.py
import sys
print(sys.argv)
Writing args.py

There is a special variable argv in the sys module that holds all the command-line arguments passed to the program.

In [24]:
!python args.py hello world
['args.py', 'hello', 'world']

By convention, the first element of sys.argv is the program name.

In [25]:
!python args.py hello
['args.py', 'hello']
In [26]:
!python args.py "hello world"
['args.py', 'hello world']
In [27]:
!python args.py 1 2 3 4 5
['args.py', '1', '2', '3', '4', '5']

Remember that the arguments will always be strings.

Example: echo program

Let us write a simple echo program that prints back the first command-line argument.

In [28]:
%%file echo.py
import sys
print(sys.argv[1])
Writing echo.py
In [29]:
!python echo.py hello
hello
In [30]:
# even if we pass more arguments, it prints only the first one.
!python echo.py hello world
hello

Q: How to print two arguments?

In [31]:
%%file echo2.py
import sys
print(sys.argv[1], sys.argv[2])
Writing echo2.py
In [32]:
!python echo2.py hello world
hello world
In [33]:
%%file echo2a.py
import sys
arg1 = sys.argv[1]
arg2 = sys.argv[2]
print(arg1, arg2)
Writing echo2a.py
In [34]:
!python echo2a.py hello world
hello world

Problem: Write a program square.py that takes a number as command-line argument and prints its square.

$ python square.py 4
16
In [37]:
%%file square.py
import sys
n = int(sys.argv[1])
print(n*n)
Overwriting square.py
In [38]:
!python square.py 4
16

Conditional Expressions

In [39]:
age = 30
In [40]:
age > 25
Out[40]:
True
In [41]:
age <= 25
Out[41]:
False
In [42]:
name = "Alice"
In [43]:
name == "Alice"
Out[43]:
True
In [44]:
name == "Alice" and age > 25
Out[44]:
True

Python has in operator to check if an element is part of another.

In [45]:
"hell" in "hello"
Out[45]:
True
In [47]:
"yell" in "hello"
Out[47]:
False
In [48]:
"yell" not in "hello"
Out[48]:
True
In [49]:
"a" in ["a", "b", "c", "d"]
Out[49]:
True
In [50]:
vowels = ["a", "e", "i", "o", "u"]
def is_vowel(c):
    return c in vowels
In [51]:
is_vowel('x')
Out[51]:
False
In [52]:
is_vowel('e')
Out[52]:
True

Strings have some useful methods for prefix and suffix checking.

In [54]:
"hello".startswith("hell")
Out[54]:
True
In [55]:
"hello".endswith("lo")
Out[55]:
True
In [56]:
def is_python_file(filename):
    return filename.endswith(".py")
In [57]:
is_python_file("square.py")
Out[57]:
True
In [58]:
is_python_file("square.c")
Out[58]:
False

The if Statement

In [59]:
n = 34
if n % 2 == 0:
    print("even")
else:
    print("odd")
even
In [60]:
def check_even(n):
    if n % 2 == 0:
        print("even")
    else:
        print("odd")
In [61]:
check_even(34)
even
In [62]:
check_even(35)
odd
In [63]:
def check_number(n):
    if n < 10:
        print(n, "is a single digit number")
    elif n < 100:
        print(n, "is a two digit number")
    else:
        print(n, "is a big number")
In [64]:
check_number(4)
4 is a single digit number
In [65]:
check_number(45)
45 is a two digit number
In [66]:
check_number(456)
456 is a big number

Problem: Write a function minimum to compute minimum of two numbers, without using the built-in function min. The function should return the minimum of the two numbers passed as arguments to it.

>>> minimum(3, 5)
3
>>> minimum(5, 3)
3
>>> minimum(3, 5) + 1
4

Let us look at the following program.

In [67]:
def square1(x):
    return x*x

def square2(x):
    print(x*x)

print(square1(4))
square2(4)
16
16

Which of the above two functions is better and why?

When a function returns a value, it can be used in other computations.

In [69]:
x = square1(4)
print(x+1)
17
In [70]:
square1(4) + 1
Out[70]:
17
In [71]:
square1(square1(4))
Out[71]:
256
In [72]:
def minimum(x, y):
    if x < y:
        return x
    else:
        return y
In [73]:
minimum(3, 4)
Out[73]:
3
In [74]:
minimum(4, 3)
Out[74]:
3
In [76]:
minimum(3, 4) + 10
Out[76]:
13

Lists

In [77]:
x = ["a", "b", "c", "d"]
In [78]:
len(x)
Out[78]:
4
In [79]:
x[0]
Out[79]:
'a'
In [80]:
x[1]
Out[80]:
'b'

For Loop

In [81]:
for a in x:
    print(a)
a
b
c
d

Remember that the body of for is indented.

In [82]:
names = ["Alice", "Bob", "Charlie"]
In [83]:
for name in names:
    print("Hello", name)
Hello Alice
Hello Bob
Hello Charlie

Python has a built-in function range to iterate over a sequence of numbers.

In [84]:
for i in range(5):
    print(i)
0
1
2
3
4

range(n) gives numbers from 0 ... n-1.

In [85]:
# numbers from 2 to 5 (end is not included)
for i in range(2, 5):
    print(i)
2
3
4
In [86]:
def say_hello(name, n):
    for i in range(n):
        print("Hello", name)
In [87]:
say_hello("Python", 4)
Hello Python
Hello Python
Hello Python
Hello Python

Problem: The os.listdir function takes path to a directory as argument and returns all the files in that directory as a list. Write a program ls.py that takes path to a directory as command-line argument and prints files in that directory, one per line.

$ python ls.py .
date.py
day1.ipynb
day1.html
day2.ipynb
day2.html
square.py

$ python ls.py /tmp
a.txt
b.txt

Example: Counting sum of numbers

Python has a built-in function sum to compute sum of a list of numbers.

In [88]:
sum([1, 2, 3, 4])
Out[88]:
10

Let us try to implement our own sum function.

In [99]:
def my_sum(numbers):
    result = 0
    #print("BEGIN my_sum", numbers)
    #print("result 0")
    for n in numbers:
        result = result + n
        #print("n", n)
        #print("result", result)
    return result
In [100]:
my_sum([1, 2, 3, 4])
Out[100]:
10
In [102]:
# sum of all numbers below a million
my_sum(range(100000))
Out[102]:
4999950000

Problem: Write a function product to compute product of given numbers.

>>> product([1, 2, 3, 4])
24

Modifying and Growing Lists

In [104]:
x = ["a", "b", "c", "d"]
In [105]:
x
Out[105]:
['a', 'b', 'c', 'd']
In [106]:
x[1]
Out[106]:
'b'
In [107]:
x[1] = 'bb'
In [108]:
x
Out[108]:
['a', 'bb', 'c', 'd']
In [109]:
x.append('e')
In [110]:
x
Out[110]:
['a', 'bb', 'c', 'd', 'e']

Remember that the append method doesn't return anything, but modifies the list in-place.

Example: squares

Let us write a program to compute squares of all numbers in a list.

In [111]:
def squares(numbers):
    result = []
    for n in numbers:
        result.append(n*n)
    return result
In [112]:
print(squares([1, 2, 3, 4]))
[1, 4, 9, 16]
In [113]:
# sum of squares of all numbers below one million
sum(squares(range(1000000)))
Out[113]:
333332833333500000

Problem: Write a function evens that takes a list of numbers as arguments and returns a new list containing only the even numbers from it.

>>> evens([1, 2, 3, 4, 5, 6])
[2, 4, 6]
In [114]:
def evens(numbers):
    result = []
    for n in numbers:
        if n % 2 == 0:
            result.append(n)
    return result
In [115]:
evens([1, 2, 3, 4, 5, 6])
Out[115]:
[2, 4, 6]

List Comprehensions

In [116]:
x = [1, 2, 3, 4, 5, 6]
In [117]:
[a*a for a in x]
Out[117]:
[1, 4, 9, 16, 25, 36]
In [118]:
[a*a for a in x if a%2 == 0]
Out[118]:
[4, 16, 36]

Compute sum of squares of all numbers below a million.

In [119]:
sum([n*n for n in range(1000000)])
Out[119]:
333332833333500000

List comprehensions are usually written as:

[expr for var in alist]

This creates a new list.

In [120]:
def squares(numbers):
    result = [n*n for n in numbers]
    return result
In [121]:
squares([1, 2, 3, 4])
Out[121]:
[1, 4, 9, 16]
In [123]:
def squares(numbers):
    return [n*n for n in numbers]

print(squares([1, 2, 3, 4]))
[1, 4, 9, 16]

Problem: Write a function list_pyfiles that takes path a directory as argument and returns all python files from that directory.

>>> list_pyfiles(".")
['square.py', 'args.py', 'echo.py']
In [125]:
numbers = [1, 2, 3, 4, 5, 6]
even_numbers = [n for n in numbers if n%2 == 0]
print(even_numbers)
[2, 4, 6]
In [126]:
import os

def list_pyfiles(path):
    files = os.listdir(path)
    return [f for f in files if f.endswith(".py")]
In [127]:
list_pyfiles(".")
Out[127]:
['args.py',
 'date.py',
 'echo.py',
 'echo2.py',
 'echo2a.py',
 'hello.py',
 'square.py']
In [128]:
def list_pyfiles(path):
    return [f for f in os.listdir(path) if f.endswith(".py")]
In [129]:
list_pyfiles(".")
Out[129]:
['args.py',
 'date.py',
 'echo.py',
 'echo2.py',
 'echo2a.py',
 'hello.py',
 'square.py']

There is os.path.getsize function to find size of a file.

In [130]:
os.path.getsize("args.py")
Out[130]:
26

How to compute total size of all python files in the current directory?

In [132]:
[f for f in os.listdir(".") if f.endswith(".py")]
Out[132]:
['args.py',
 'date.py',
 'echo.py',
 'echo2.py',
 'echo2a.py',
 'hello.py',
 'square.py']
In [133]:
[os.path.getsize(f) for f in os.listdir(".") if f.endswith(".py")]
Out[133]:
[26, 33, 29, 42, 66, 33, 42]
In [134]:
sum([os.path.getsize(f) for f in os.listdir(".") if f.endswith(".py")])
Out[134]:
271

Iteration Patterns

Iterating over a list of values

In [139]:
x = ["a", "b", "c", "d"]
In [140]:
for c in x:
    print(c, c.upper())
a A
b B
c C
d D
In [141]:
[c.upper() for c in x]
Out[141]:
['A', 'B', 'C', 'D']

Iterating over a sequence of numbers

In [142]:
for i in range(5):
    print(i, i*i)
0 0
1 1
2 4
3 9
4 16
In [143]:
for i in range(2, 5):
    print(i, i*i)
2 4
3 9
4 16
In [144]:
[i*i for i in range(5)]
Out[144]:
[0, 1, 4, 9, 16]

Iterating over two lists together

Given names and scores of students, how to print name and score of each student.

In [145]:
names = ["A", "B", "C", "D"]
scores = [10, 20, 30, 40]
In [146]:
for name, score in zip(names, scores):
    print(name, score)
A 10
B 20
C 30
D 40
In [147]:
zip(names, scores)
Out[147]:
<zip at 0x10405b6c8>
In [148]:
list(zip(names, scores))
Out[148]:
[('A', 10), ('B', 20), ('C', 30), ('D', 40)]

Q: What happens if one list is shorter?

In [150]:
list(zip(["A", "B", "C", "D"], [10, 20, 30]))
Out[150]:
[('A', 10), ('B', 20), ('C', 30)]

It considers the smallest length.

Q: Is it possible to zip more than two lists?

Yes.

In [152]:
names
Out[152]:
['A', 'B', 'C', 'D']
In [153]:
list(zip(names, names, names))
Out[153]:
[('A', 'A', 'A'), ('B', 'B', 'B'), ('C', 'C', 'C'), ('D', 'D', 'D')]

Problem: Write a function vector_add to add two vectors.

>>> vector_add([1, 2, 3, 4], [10, 20, 30, 40])
[11, 22, 33, 44]
In [154]:
# traditional way
def vector_add(A, B):
    C = []
    for a, b in zip(A, B):
        C.append(a+b)
    return C
In [155]:
vector_add([1, 2, 3, 4], [10, 20, 30, 40])
Out[155]:
[11, 22, 33, 44]
In [156]:
# list-comprehensions approach
def vector_add(A, B):
    return [a+b for a, b in zip(A, B)]
In [157]:
vector_add([1, 2, 3, 4], [10, 20, 30, 40])
Out[157]:
[11, 22, 33, 44]

Iterating over index and the element together

In [158]:
names = ["a", "b", "c", "d"]
In [159]:
for i, name in enumerate(names):
    print(i, name)
0 a
1 b
2 c
3 d
In [160]:
list(enumerate(names))
Out[160]:
[(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd')]
In [161]:
chapters = ["Getting Started", "Lists", "Working with Files"]
In [163]:
for i, title in enumerate(chapters):
    print("Chapter", i+1, ":", title)
Chapter 1 : Getting Started
Chapter 2 : Lists
Chapter 3 : Working with Files
In [164]:
for i, title in enumerate(chapters, start=1):
    print("Chapter", i, ":", title)
Chapter 1 : Getting Started
Chapter 2 : Lists
Chapter 3 : Working with Files

List Indexing

In [165]:
x = ["a", "b", "c", "d", "e"]
In [166]:
x[0]
Out[166]:
'a'
In [167]:
x[1]
Out[167]:
'b'

How to get the last element of x?

In [168]:
x[len(x)-1]
Out[168]:
'e'

Python has a short hand for that.

In [170]:
x[-1] # last element
Out[170]:
'e'
In [171]:
x[-2] # second last element
Out[171]:
'd'

Let us try an example.

In [172]:
def get_last_word(sentence):
    return sentence.split()[-1]
In [173]:
get_last_word("one two three")
Out[173]:
'three'

List Slicing

In [174]:
x = ["a", "b", "c", "d", "e", "f", "g", "h"]
In [175]:
x[0:2]
Out[175]:
['a', 'b']
In [176]:
x[:2] # up to index 2
Out[176]:
['a', 'b']

The end in not included.

In [177]:
x[2:] # index 2 onwards
Out[177]:
['c', 'd', 'e', 'f', 'g', 'h']
In [178]:
x[2:6] # from index 2 to index 6
Out[178]:
['c', 'd', 'e', 'f']
In [179]:
x[1:6:2] # take every second element from index 1 to 6
Out[179]:
['b', 'd', 'f']
In [180]:
x[:] # copy of the list
Out[180]:
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
In [181]:
x[::-1] # reverse the list
Out[181]:
['h', 'g', 'f', 'e', 'd', 'c', 'b', 'a']

Example: echo2.py

Earlier we wrote a program to print the first command-line argument. Let us improve that to print all command-line arguments.

In [188]:
%%file echo2.py
import sys
#print(sys.argv)
args = sys.argv[1:]
#print(args)
print(" ".join(args))
Overwriting echo2.py
In [189]:
!python echo2.py hello world
hello world
In [ ]: