Python Virtual Training For Arcesium - Module II - Day 3¶

Feb 13-17, 2023 Vikrant Patil

All notes are available online at https://notes.pipal.in/2023/arcesium_finop_jan/

Please login to https://engage.pipal.in/ and launch jupyter lab

For today create a notebook with name module2-day3

notebook names are case sensitive. Make sure you give correct name

© Pipal Academy LLP

In [ ]:
 
  • Write a function tables that generates multiplication tables from 1 to 10 and writes it in csv file.
>>> tables("tables.csv")
>>> print_contents("table.csv")
1,2,3,4,5,6,7,8,9,10
2,4,6,....
In [8]:
def generate_table(x):
    return [x*i for i in range(1, 11)]

def generate_tables(n):
    return [generate_table(i) for i in range(1, n+1)]
In [2]:
generate_table(2)
Out[2]:
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
In [3]:
generate_table(11)
Out[3]:
[11, 22, 33, 44, 55, 66, 77, 88, 99, 110]
In [4]:
generate_tables(5)
Out[4]:
[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
 [2, 4, 6, 8, 10, 12, 14, 16, 18, 20],
 [3, 6, 9, 12, 15, 18, 21, 24, 27, 30],
 [4, 8, 12, 16, 20, 24, 28, 32, 36, 40],
 [5, 10, 15, 20, 25, 30, 35, 40, 45, 50]]
In [6]:
def generate_tables(n):
    tables = []
    for i in range(1, n+1):# numbers for which we want to generate tables
        t = []
        for m in range(1, 11):
            t.append(i*m)
        
        tables.append(t)
    return tables
In [7]:
generate_tables(6)
Out[7]:
[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
 [2, 4, 6, 8, 10, 12, 14, 16, 18, 20],
 [3, 6, 9, 12, 15, 18, 21, 24, 27, 30],
 [4, 8, 12, 16, 20, 24, 28, 32, 36, 40],
 [5, 10, 15, 20, 25, 30, 35, 40, 45, 50],
 [6, 12, 18, 24, 30, 36, 42, 48, 54, 60]]
In [9]:
def writecsv(data2d, filename):
    with open(filename, "w") as f:
        for row in data2d:
            line = ",".join([str(item) for item in row])
            f.write(line)
            f.write("\n")
            
            
In [10]:
writecsv(generate_tables(5), "tables5.csv")
In [11]:
!python cat.py tables5.csv
1,2,3,4,5,6,7,8,9,10
2,4,6,8,10,12,14,16,18,20
3,6,9,12,15,18,21,24,27,30
4,8,12,16,20,24,28,32,36,40
5,10,15,20,25,30,35,40,45,50
In [13]:
t6 = generate_tables(6)
In [14]:
t6
Out[14]:
[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
 [2, 4, 6, 8, 10, 12, 14, 16, 18, 20],
 [3, 6, 9, 12, 15, 18, 21, 24, 27, 30],
 [4, 8, 12, 16, 20, 24, 28, 32, 36, 40],
 [5, 10, 15, 20, 25, 30, 35, 40, 45, 50],
 [6, 12, 18, 24, 30, 36, 42, 48, 54, 60]]
In [16]:
t6[0]
Out[16]:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
In [17]:
strdata = []
for item in t6[0]:
    strdata.append(str(item))
In [18]:
strdata
Out[18]:
['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
In [19]:
",".join(strdata)
Out[19]:
'1,2,3,4,5,6,7,8,9,10'
In [22]:
strdata = []
for item in t6[0]:
    strdata.append(str(item))

def writecsv(data2d, filename):
    with open(filename, "w") as f:    
        for row in data2d:
            line = ",".join([str(item) for item in row])
            f.write(line)
            f.write("\n")
In [23]:
writecsv(t6, "tables6.csv")
In [24]:
!python cat.py tables6.csv
1,2,3,4,5,6,7,8,9,10
2,4,6,8,10,12,14,16,18,20
3,6,9,12,15,18,21,24,27,30
4,8,12,16,20,24,28,32,36,40
5,10,15,20,25,30,35,40,45,50
6,12,18,24,30,36,42,48,54,60
In [28]:
def writecsv(data2d, headers, filename):
    with open(filename, "w") as f: 
        
        headerline = ",".join(headers)
        f.write(headerline)
        f.write("\n")
        
        for row in data2d:
            line = ",".join([str(item) for item in row])
            f.write(line)
            f.write("\n")
In [29]:
writecsv(t6, list("ABCDEFGHIJ"), "tables6.csv")
In [30]:
def tables(n, filename):
    t = generate_tables(n)
    headers = [f"multiflier-{i}" for i in range(1, 11)]
    writecsv(t, headers,filename) 
In [31]:
tables(8, "tables8.csv")

Write a program center_align.py to center align all lines in the given file.

$ cat poem.txt
There was an Old Man with a beard
Who said, "It is just as I feared!
Two Owls and a Hen,
Four Larks and a Wren,
Have all built their nests in my beard!"

$ python center_align.py poem.txt
   There was an Old Man with a beard
   Who said, "It is just as I feared!
          Two Owls and a Hen,
         Four Larks and a Wren,
Have all built their nests in my beard!"
In [48]:
%%file center_align.py
import typer
app = typer.Typer()

@app.command()
def center_align(filename:str):
    with open(filename) as f:
        maxlen = 0
        for line in f:
            if len(line) > maxlen:
                maxlen = len(line)
                
    with open(filename) as f:
        for line in f:
            print(line.center(maxlen), end="")
                  
                  
if __name__ == "__main__":
    app()
    
Overwriting center_align.py
In [42]:
"text data which is not aligned to center".center(40)
Out[42]:
'text data which is not aligned to center'
In [46]:
!python center_align.py --help
Usage: center_align.py [OPTIONS] FILENAME

Arguments:
  FILENAME  [required]

Options:
  --install-completion [bash|zsh|fish|powershell|pwsh]
                                  Install completion for the specified shell.
  --show-completion [bash|zsh|fish|powershell|pwsh]
                                  Show completion for the specified shell, to
                                  copy it or customize the installation.
  --help                          Show this message and exit.
In [47]:
!python center_align.py poem.txt
                  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!
   
In [50]:
%%file center_align1.py
import typer
app = typer.Typer()

@app.command()
def center_align(filename:str):
    with open(filename) as f:
        maxlen = 0
        for line in f:
            if len(line) > maxlen:
                maxlen = len(line)
                
    with open(filename) as f:
        for line in f:
            print(line.strip().center(maxlen))
                  
                  
if __name__ == "__main__":
    app()
    
Writing center_align1.py
In [51]:
!python center_align1.py poem.txt
                   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!   
In [52]:
%%file center_align2.py
import typer
app = typer.Typer()

@app.command()
def center_align(filename:str):
    with open(filename) as f:
        maxlen = 0
        for line in f:
            if len(line) > maxlen:
                maxlen = len(line)-1
                
    with open(filename) as f:
        for line in f:
            print(line.strip().center(maxlen))
                  
                  
if __name__ == "__main__":
    app()
    
Writing center_align2.py
In [54]:
!python center_align2.py poem.txt
                   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!  
In [ ]:
 
In [55]:
x = "fsdfsdfdsfdsf sdsad dsadsa \n"
In [56]:
x.center(100)
Out[56]:
'                                    fsdfsdfdsfdsf sdsad dsadsa \n                                    '
In [57]:
print(x.center(100))
                                    fsdfsdfdsfdsf sdsad dsadsa 
                                    
In [58]:
%%file center_align3.py
import typer
app = typer.Typer()

@app.command()
def center_align(filename:str):
    with open(filename) as f:
        longest_line = max(f, key=len)
        maxlen = len(longest_line) - 1
                
    with open(filename) as f:
        for line in f:
            print(line.strip().center(maxlen))
                  
                  
if __name__ == "__main__":
    app()
Writing center_align3.py
In [59]:
!python center_align3.py poem.txt
                   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!  

classes and OOPS¶

In [60]:
object_list  = [1, 2, 3, 4, 5, 6, 7, 8]
In [63]:
object_list.index(2) # method to access data
Out[63]:
1
In [64]:
object_list.append(0) # method to manipulate data

Bank Account¶

In [65]:
%%file bank0.py

balance = 0

# methods
def withdraw(amount):
    global balance # this tells this function that treat balance as global variable, don't create local
    balance = balance - amount

def deposit(amount):
    global balance
    balance = balance + amount
    
def get_balance(amount):
    return balance # there is no need of declaring balance as global
    # because if any variable is not defined locally, it automatically reads global variable
Writing bank0.py
In [66]:
import bank0
In [67]:
bank0.deposit(5000)
In [68]:
bank0.get_balance()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[68], line 1
----> 1 bank0.get_balance()

TypeError: get_balance() missing 1 required positional argument: 'amount'
In [70]:
%%file bank1.py

balance = 0

# methods
def withdraw(amount):
    global balance # this tells this function that treat balance as global variable, don't create local
    balance = balance - amount

def deposit(amount):
    global balance
    balance = balance + amount
    
def get_balance():
    return balance # there is no need of declaring balance as global
    # because if any variable is not defined locally, it automatically reads global variable
Overwriting bank1.py
In [71]:
import bank1
In [72]:
bank1.get_balance()
Out[72]:
0
In [73]:
bank1.deposit(5000)
In [74]:
bank1.get_balance()
Out[74]:
5000
In [75]:
bank1.withdraw(3000)
In [76]:
bank1.get_balance()
Out[76]:
2000
In [77]:
%%file bank2.py

def make_account(balance):
    account = {"balance":balance}
    return account

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

def deposit(account, amount):
    account['balance'] += amount
    
def get_balance(account):
    return account['balance']
Writing bank2.py
In [78]:
import bank2
In [80]:
acc1 = bank2.make_account(500)
acc2 = bank2.make_account(5000)
In [81]:
bank2.get_balance(acc1)
Out[81]:
500
In [83]:
bank2.deposit(acc1, 3000)
In [84]:
bank2.get_balance(acc1)
Out[84]:
3500
In [85]:
bank2.get_balance(acc2)
Out[85]:
5000
In [86]:
bank2.withdraw(acc2, 300)
In [87]:
bank2.get_balance(acc2)
Out[87]:
4700
In [88]:
bank2.get_balance(acc1)
Out[88]:
3500
In [97]:
class BankAccount:
    
    def __init__(self): 
        self.balance = 0   
        
    def get_balance(self):
        return self.balance
    
    def deposit(self, amount):
        self.balance += amount
        
    def withdraw(self, amount):
        self.balance -= amount
In [91]:
A1 = BankAccount() #__init__ has only self argument
In [92]:
A1.get_balance() # you don't pass self
Out[92]:
0
In [93]:
A1.deposit(5000)
In [94]:
A1.get_balance()
Out[94]:
5000
In [98]:
class BankAccount1:
    
    def __init__(self, balance): 
        self.balance = balance  
        
    def get_balance(self):
        return self.balance
    
    def deposit(self, amount):
        self.balance += amount
        
    def withdraw(self, amount):
        self.balance -= amount
In [99]:
A_ = BankAccount1()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[99], line 1
----> 1 A_ = BankAccount1()

TypeError: BankAccount1.__init__() missing 1 required positional argument: 'balance'
In [100]:
A_ = BankAccount1(4000)
In [101]:
A_.get_balance()
Out[101]:
4000
In [102]:
a = BankAccount1(3000)
b = BankAccount1(5000)
c = BankAccount1(7000)
In [103]:
type(a)
Out[103]:
__main__.BankAccount1
In [104]:
type([1 , 2, 3, 4])
Out[104]:
list
In [105]:
isinstance(1, int)
Out[105]:
True
In [106]:
isinstance("1", int)
Out[106]:
False
In [107]:
isinstance(a, BankAccount1)
Out[107]:
True
In [108]:
isinstance(a, BankAccount)
Out[108]:
False
In [109]:
0
Out[109]:
0
In [110]:
def add():
    pass
In [111]:
add
Out[111]:
<function __main__.add()>
In [112]:
type(add)
Out[112]:
function
In [113]:
with open("poem.txt") as f:
    print(type(f))
<class '_io.TextIOWrapper'>
In [119]:
class BankAccount:
    
    def __init__(self, balance): # constructor
        self.minimum_balance = 1000
        if balance < self.minimum_balance:
            raise Exception(f"To start this account, you need to have minimum {self.minimum_balance} balance")
        self.balance = balance
        
        
    def get_balance(self):
        return self.balance
    
    def deposit(self, amount):
        self.balance += amount
        
    def withdraw(self, amount):
        if self.balance - amount <= self.minimum_balance:
            raise Exception(f"You can withdraw , beacuase your balance can not be below {self.minimum_balance}")
        self.balance -= amount
        
        
class ZeroBalanceAccount(BankAccount):
    
    def __init__(self):
        self.balance = 0
        
    def withdraw(self, amount):
        self.balance -= amount
        
    
In [120]:
b1 = BankAccount(500)
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
Cell In[120], line 1
----> 1 b1 = BankAccount(500)

Cell In[119], line 6, in BankAccount.__init__(self, balance)
      4 self.minimum_balance = 1000
      5 if balance < self.minimum_balance:
----> 6     raise Exception(f"To start this account, you need to have minimum {self.minimum_balance} balance")
      7 self.balance = balance

Exception: To start this account, you need to have minimum 1000 balance
In [121]:
z1 = ZeroBalanceAccount()
In [122]:
z1.get_balance()
Out[122]:
0
In [123]:
b1 = BankAccount(1500)
In [124]:
b1.withdraw(700)
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
Cell In[124], line 1
----> 1 b1.withdraw(700)

Cell In[119], line 18, in BankAccount.withdraw(self, amount)
     16 def withdraw(self, amount):
     17     if self.balance - amount <= self.minimum_balance:
---> 18         raise Exception(f"You can withdraw , beacuase your balance can not be below {self.minimum_balance}")
     19     self.balance -= amount

Exception: You can withdraw , beacuase your balance can not be below 1000
In [125]:
z1.deposit(1000)
In [126]:
z1.withdraw(400)
In [127]:
z1.get_balance()
Out[127]:
600
In [128]:
isinstance(z1, ZeroBalanceAccount)
Out[128]:
True
In [129]:
isinstance(z1, BankAccount)
Out[129]:
True
In [130]:
class Foo:
    pass
In [131]:
f = Foo() 
In [132]:
type(f)
Out[132]:
__main__.Foo

problem

  • Write a class Light
      Light
+---------------+
|               |<---------switch_on
|    on=True    |
|               |<---------switch_off
+---------------+
   |
   |
   +------<------- is_on
In [133]:
BankAccount
Out[133]:
__main__.BankAccount
In [137]:
BankAccount.deposit(500) # it took 500 as self!
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[137], line 1
----> 1 BankAccount.deposit(500) # it took 500 as self!

TypeError: BankAccount.deposit() missing 1 required positional argument: 'amount'
In [135]:
acc = BankAccount(6000)
In [138]:
acc.deposit(500) # instance.deosite... self will be passed by python,, 500 is passed as amount
In [139]:
acc.get_balance()
Out[139]:
6500
In [141]:
BankAccount
Out[141]:
__main__.BankAccount
In [142]:
acc
Out[142]:
<__main__.BankAccount at 0x7f5dab759750>
In [144]:
type(BankAccount) # its a class
Out[144]:
type
In [145]:
type(acc)
Out[145]:
__main__.BankAccount
In [146]:
acc.get_balance()
Out[146]:
6500
In [151]:
class FooBar:
    
    def method(): # no self is given!
        print("hello")
In [148]:
fb = FooBar()
In [150]:
fb.method() # self will be passed automatically
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[150], line 1
----> 1 fb.method()

TypeError: FooBar.method() takes 0 positional arguments but 1 was given
In [153]:
class Light:
    
    def __init__(self):
        self.state = False
        
        
    def is_on(self):
        return self.state == True
    
    def switch_off(self):
        if self.state: # if it is on
            self.state = False
            
    def switch_on(self):
        if not self.state: #if it is off
            self.state = True
            
    def toggle(self):
        pass # above check is required only if we have some method like this
In [154]:
l = Light()
In [155]:
l.is_on()
Out[155]:
False
In [156]:
l.switch_on()
In [157]:
l.is_on()
Out[157]:
True
In [158]:
all_lights_in_house = [Light(), Light(), Light(), Light()]
In [159]:
def switch_on_all(lights):
    for l in lights:
        l.switch_on()
        
In [160]:
all_lights_in_house
Out[160]:
[<__main__.Light at 0x7f5dab710bb0>,
 <__main__.Light at 0x7f5dab713d00>,
 <__main__.Light at 0x7f5dab712b90>,
 <__main__.Light at 0x7f5dab713610>]
In [161]:
def print_status(lights):
    for l in lights:
        print(l.is_on())
In [162]:
print_status(all_lights_in_house)
False
False
False
False
In [163]:
switch_on_all(all_lights_in_house)
In [164]:
print_status(all_lights_in_house)
True
True
True
True
In [ ]: