Module II - Day 4

Python Made Easy: Science and Math Edition

Sep-Dec 2025 batch, Vikrant Patil

Date: 06 Dec 2025

Click here for All Notes

Live note are here https://vikrant.dev/python-made-easy-science-math/module2-day4.html

Please login to https://traininghub.vikrant.dev and create a notebook with name module2-day4.ipynb

© Vikrant Patil

Interation Patterns

nums = [2, 4, 5, 6, 2, 4, 6]
for index, item  in enumerate(nums):
    print(index, item)
0 2
1 4
2 5
3 6
4 2
5 4
6 6
with open("zen.txt") as f:
    for linenum, line in enumerate(f, start=1):
        print(linenum, line,  end="")
1 The Zen of Python, by Tim Peters
2 
3 Beautiful is better than ugly.
4 Explicit is better than implicit.
5 Simple is better than complex.
6 Complex is better than complicated.
7 Flat is better than nested.
8 Sparse is better than dense.
9 Readability counts.
10 Special cases aren't special enough to break the rules.
11 Although practicality beats purity.
12 Errors should never pass silently.
13 Unless explicitly silenced.
14 In the face of ambiguity, refuse the temptation to guess.
15 There should be one-- and preferably only one --obvious way to do it.
16 Although that way may not be obvious at first unless you're Dutch.
17 Now is better than never.
18 Although never is often better than *right* now.
19 If the implementation is hard to explain, it's a bad idea.
20 If the implementation is easy to explain, it may be a good idea.
21 Namespaces are one honking great idea -- let's do more of those!
nums
[2, 4, 5, 6, 2, 4, 6]
for i in reversed(nums):
    print(i)
6
4
2
6
5
4
2
nums[::-1] # this will create new list
[6, 4, 2, 6, 5, 4, 2]
firstname, secondname = ["Alice", "Alex", "Hans"], ["Wondergirl", "Lion", "Solo"]
firstname
['Alice', 'Alex', 'Hans']
secondname
['Wondergirl', 'Lion', 'Solo']
l = len(firstname)
for i in range(l):
    print(firstname[i], secondname[i])
Alice Wondergirl
Alex Lion
Hans Solo
for first, second in zip(firstname, secondname):
    print(first, second)
Alice Wondergirl
Alex Lion
Hans Solo
firstname
['Alice', 'Alex', 'Hans']
secondname
['Wondergirl', 'Lion', 'Solo']
list(zip(firstname,  secondname))
[('Alice', 'Wondergirl'), ('Alex', 'Lion'), ('Hans', 'Solo')]
for first, second in zip(firstname, secondname):
    print(first, second)
Alice Wondergirl
Alex Lion
Hans Solo
X = [1, 2, 3, 4, 5, 6]
Y = [-1, -2, -3, -4]
for x, y in zip(X, Y):
    print(x, y)
1 -1
2 -2
3 -3
4 -4
X = [1, 2, 3, 4, 5, 6]
Y = [-1, -2, -3, -4]
Z = [0, 1, 2, 3, 4, 5]
for x,y,z in zip(X, Y, Z):
    print(x, y, z)
1 -1 0
2 -2 1
3 -3 2
4 -4 3

Theses are iterator - iterators are kind of pointer which keeps moving as you consume - So once consumed they will behave like empty data

rnums = reversed(nums) 
rnums
<list_reverseiterator at 0x7f5ec6cdac20>
nums
[2, 4, 5, 6, 2, 4, 6]
next(rnums) # we consumed first item from iterator
6
next(rnums)
4
next(rnums)
2
next(rnums)
6
next(rnums)
5
next(rnums)
4
next(rnums)
2
next(rnums)
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
Cell In[32], line 1
----> 1 next(rnums)

StopIteration: 
for i in rnums: # behave like empty loop
    print(i)
for i in reversed(nums):
    print(i, end=",")
6,4,2,6,5,4,2,
with open("zen.txt") as f:
    for line in f: # here f is actually iterator
        print(line, end="")
    # after the for loop reading of file is over..iterator is consumed
    print("After for loop: ", f.readline()) # iterator is empty! so this will result in ""
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!
After for loop:  
%%file test_exception.py

nums = [2, 3, 4]
enumnums = enumerate(nums)
print(next(enumnums))
print(next(enumnums))
print(next(enumnums))
next(enumnums) # stop iteration
print("Hello")
Overwriting test_exception.py
!python test_exception.py
(0, 2)

(1, 3)

(2, 4)

Traceback (most recent call last):

  File "/home/vikrant/programming/work/github/vikrant.dev/python-made-easy-science-math/test_exception.py", line 7, in <module>

    next(enumnums) # stop iteration

    ~~~~^^^^^^^^^^

StopIteration

List Comprehensions

sqrs = []
for n in nums:
    sqrs.append(n*n)
sqrs
[4, 16, 25, 36, 4, 16, 36]
sqrs_list = [n*n for n in nums]
sqrs_list
[4, 16, 25, 36, 4, 16, 36]
[n**3 for n in nums]
[8, 64, 125, 216, 8, 64, 216]
%%file stocks.csv
symbol,value,gain
INFY,2000,5.0
TATA,567,3.5
SIMPLES,1534,20.0
MM,234,-5
Writing stocks.csv
with open("stocks.csv") as f:
    for line in f:
        print(line.strip().split(",")[0])
symbol
INFY
TATA
SIMPLES
MM
with open("stocks.csv") as f:
    firstcolumn = [line.strip().split(",")[0] for line in f]
firstcolumn
['symbol', 'INFY', 'TATA', 'SIMPLES', 'MM']
def extract_column(filepath, col):
    with open(filepath) as f:
        headers = f.readline() # we read first line!
        return [line.strip().split(",")[col] for line in f]
extract_column("stocks.csv", 0)
['INFY', 'TATA', 'SIMPLES', 'MM']
extract_column("stocks.csv", 1)
['2000', '567', '1534', '234']
extract_column("stocks.csv", 2)
['5.0', '3.5', '20.0', '-5']
nums
[2, 4, 5, 6, 2, 4, 6]
[n*n for n in nums]
[4, 16, 25, 36, 4, 16, 36]

list comprehensions - filtering

[n*n for n in nums if n%2==0]
[4, 16, 36, 4, 16, 36]
def even(n):
    return n%2==0

def odd(n):
    return not even(n)
[n*n for n in nums if odd(n)]
[25]
def compute(n):
    n*n
[compute(n) for n in nums if even(n)] # business logic
[None, None, None, None, None, None]
def compute(n):
    return n*n
[compute(n) for n in nums if even(n)] # business logic
[4, 16, 36, 4, 16, 36]

Example

indexdata = [('IBM', 'Monday', 111.71436961893693),
            ('IBM', 'Tuesday', 141.21220022208635),
            ('IBM', 'Wednesday', 112.40571010053796),
            ('IBM', 'Thursday', 137.54133351926248),
            ('IBM', 'Friday', 140.25154281801224),
            ('MICROSOFT', 'Monday', 235.0403622499107),
            ('MICROSOFT', 'Tuesday', 225.0206535036475),
            ('MICROSOFT', 'Wednesday', 216.10342426936444),
            ('MICROSOFT', 'Thursday', 200.38038844494193),
            ('MICROSOFT', 'Friday', 235.80850482793264),
            ('APPLE', 'Monday', 321.49182055844256),
            ('APPLE', 'Tuesday', 340.63612771662815),
            ('APPLE', 'Wednesday', 303.9065277507285),
            ('APPLE', 'Thursday', 338.1350605764038),
            ('APPLE', 'Friday', 318.3912296144338)]
type(indexdata)
list
indexdata[0]
('IBM', 'Monday', 111.71436961893693)
[value for name, day, value in indexdata if name=="IBM"]
[111.71436961893693,
 141.21220022208635,
 112.40571010053796,
 137.54133351926248,
 140.25154281801224]
[(name, value) for name, day, value in indexdata if name=="IBM"]
[('IBM', 111.71436961893693),
 ('IBM', 141.21220022208635),
 ('IBM', 112.40571010053796),
 ('IBM', 137.54133351926248),
 ('IBM', 140.25154281801224)]
[(name, day, value) for name, day, value in indexdata if name=="IBM"]
[('IBM', 'Monday', 111.71436961893693),
 ('IBM', 'Tuesday', 141.21220022208635),
 ('IBM', 'Wednesday', 112.40571010053796),
 ('IBM', 'Thursday', 137.54133351926248),
 ('IBM', 'Friday', 140.25154281801224)]
[(name, day, value) for name, day, value in indexdata if day=="Monday"]
[('IBM', 'Monday', 111.71436961893693),
 ('MICROSOFT', 'Monday', 235.0403622499107),
 ('APPLE', 'Monday', 321.49182055844256)]
with open("stocks.csv") as f:
    header = f.readline()
    data = [line.strip().split(",") for line in f]
data
[['INFY', '2000', '5.0'],
 ['TATA', '567', '3.5'],
 ['SIMPLES', '1534', '20.0'],
 ['MM', '234', '-5']]
text = "jasghd jhgdsfag  asdghf"
[c for c in text if c!=" "]
['j',
 'a',
 's',
 'g',
 'h',
 'd',
 'j',
 'h',
 'g',
 'd',
 's',
 'f',
 'a',
 'g',
 'a',
 's',
 'd',
 'g',
 'h',
 'f']
import os
def get_size(folder):
    "get folder size in KB"
    s = 0
    for root, dirs, files in os.walk(folder):
        for f in files:
            path = os.path.join(root, f)
            if os.path.isfile(path):
                s = s + os.path.getsize(path)
    return s
get_size("_site")
5432323
def filesize(root, f):
    path = os.path.join(root, f)
    if os.path.isfile(path):
        return os.path.getsize(path)

def get_size(folder):
    "get folder size in KB"
    s = 0
    for root, dirs, files in os.walk(folder):
        s = s + sum([filesize(root, f) for f in files])
    return s
    
get_size("_site")
5436282
def filesize(root, f):
    path = os.path.join(root, f)
    if os.path.isfile(path):
        return os.path.getsize(path)

def foldersize(root, files):
    "get folder size in KB"
    return sum(filesize(root, f) for f in files)

def get_size(folder):
    "get folder size in KB"
    return sum([foldersize(root, files) for root, dirs, files in os.walk(folder)])        

get_size("_site")
5436282
12119736
next(os.walk("."))
('.',
 ['promotions', '.quarto', '__pycache__', '_site', '.ipynb_checkpoints'],
 ['tail.py',
  'b.pdf',
  'students-module1-day4.ipynb',
  'about.qmd~',
  'index.qmd',
  'n.html',
  'say_hello.py',
  'l.html',
  'module2-day4.ipynb',
  'topics.qmd~',
  'maths_test_new.py',
  'm.html',
  'zen.txt',
  'notes.qmd',
  '_quarto.yml',
  'chaos.py',
  'students-module2-day2.ipynb',
  'notes.qmd~',
  'push',
  'c.pdf',
  'styles.css',
  'hello.py',
  'module1-day5.ipynb',
  'square.py',
  'trainer.qmd',
  'copyzen.txt',
  'maths_test.py',
  'test_prog.py',
  'power.py',
  'Untitled1.ipynb',
  'index.qmd~',
  'testargs.py',
  'head.py',
  'x.txt',
  'a.pdf',
  '_quarto.yml~',
  'welcome_multi_lingual.py',
  'Untitled.ipynb',
  '.gitignore',
  'module2-practice.ipynb',
  'students-module1-day2.ipynb',
  'module1-day1.ipynb',
  'module1-day2.ipynb',
  'problems.ipynb',
  'topics.qmd',
  'module2-day3.ipynb',
  'args1.py',
  'cat.py',
  'module2-day1.ipynb',
  'trainer.qmd~',
  'test_args.py',
  'module1-day3.ipynb',
  'chaos1.py',
  'hello_person.py',
  'revision.ipynb',
  'Makefile~',
  'helloworld.py',
  'test_exception.py',
  'poem.txt',
  'stocks.csv',
  'y.txt',
  'students-module1-day3.ipynb',
  'Makefile',
  'module1-day4.ipynb',
  'today.org',
  'z.txt'])

problem - write function listpy (just like os.listdir!) which uses list comprehension to identify py files in given directory

os.listdir(".")
['tail.py',
 'b.pdf',
 'students-module1-day4.ipynb',
 'about.qmd~',
 'index.qmd',
 'n.html',
 'say_hello.py',
 'l.html',
 'module2-day4.ipynb',
 'topics.qmd~',
 'maths_test_new.py',
 'm.html',
 'zen.txt',
 'notes.qmd',
 '_quarto.yml',
 'chaos.py',
 'students-module2-day2.ipynb',
 'notes.qmd~',
 'push',
 'c.pdf',
 'styles.css',
 'hello.py',
 'module1-day5.ipynb',
 'square.py',
 'trainer.qmd',
 'copyzen.txt',
 'maths_test.py',
 'test_prog.py',
 'power.py',
 'promotions',
 '.quarto',
 'Untitled1.ipynb',
 'index.qmd~',
 '__pycache__',
 'testargs.py',
 'head.py',
 'x.txt',
 'a.pdf',
 '_quarto.yml~',
 'welcome_multi_lingual.py',
 '_site',
 'Untitled.ipynb',
 '.gitignore',
 'module2-practice.ipynb',
 'students-module1-day2.ipynb',
 'module1-day1.ipynb',
 'module1-day2.ipynb',
 'problems.ipynb',
 'topics.qmd',
 'module2-day3.ipynb',
 'args1.py',
 'cat.py',
 'module2-day1.ipynb',
 'trainer.qmd~',
 'test_args.py',
 'module1-day3.ipynb',
 'chaos1.py',
 'hello_person.py',
 'revision.ipynb',
 'Makefile~',
 'helloworld.py',
 'test_exception.py',
 'poem.txt',
 'stocks.csv',
 'y.txt',
 'students-module1-day3.ipynb',
 'Makefile',
 '.ipynb_checkpoints',
 'module1-day4.ipynb',
 'today.org',
 'z.txt']
[f for f in os.listdir(".") if f.endswith(".py")]
['tail.py',
 'say_hello.py',
 'maths_test_new.py',
 'chaos.py',
 'hello.py',
 'square.py',
 'maths_test.py',
 'test_prog.py',
 'power.py',
 'testargs.py',
 'head.py',
 'welcome_multi_lingual.py',
 'args1.py',
 'cat.py',
 'test_args.py',
 'chaos1.py',
 'hello_person.py',
 'helloworld.py',
 'test_exception.py']
[f for f in os.listdir(".") if f.endswith(".ipynb")]
['students-module1-day4.ipynb',
 'module2-day4.ipynb',
 'students-module2-day2.ipynb',
 'module1-day5.ipynb',
 'Untitled1.ipynb',
 'Untitled.ipynb',
 'module2-practice.ipynb',
 'students-module1-day2.ipynb',
 'module1-day1.ipynb',
 'module1-day2.ipynb',
 'problems.ipynb',
 'module2-day3.ipynb',
 'module2-day1.ipynb',
 'module1-day3.ipynb',
 'revision.ipynb',
 'students-module1-day3.ipynb',
 'module1-day4.ipynb']
[f for f in os.listdir(".") if f.endswith(".txt")]
['zen.txt', 'copyzen.txt', 'x.txt', 'poem.txt', 'y.txt', 'z.txt']

[do(i) for i in collection(i) if cond(i)]

before loop make use of loop variable to do something and after loop make use of loop variable to put conditon

problem

  • find sum of all multiples of 7 or 11 below 1000.
  • There is a string “abrakadabra”, we want to capitalize alternate character from it. how can we do it? can a list comprehension be used to do this?

prime numbers

  • Write a function factors which finds all factors of given number (include 1 and self)
  • Write a function is_prime which checks if given number is prime based on fact that prime number has only two factors 1 and self.
  • Write a list comprehension to generate prime numbers.
sum([i for i in range(1000) if i%7==0 or i%11==0])
110110
text = "abrakadabra"
text[::2] 
'arkdba'
text[1::2] 
'baaar'
alt1 = text[::2] 
alt2 = text[1::2]baaar'
[x + y for x,y in zip(alt1, alt2.upper())]
['aB', 'rA', 'kA', 'dA', 'bR']
"".join([x + y for x,y in zip(alt1, alt2.upper())])
'aBrAkAdAbR'