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

Jun Jul 18-22, 2022 Vikrant Patil

All notes are available online at https://notes.pipal.in/2022/arcesium_finop_batch1/

Please accept the invitation that you have received in your email and login to

https://engage.pipal.in/

From there launch your jupyter lab. Create a notebook with name module2-day3

© Pipal Academy LLP

Why Classes¶

Suppose we want to model bank account

What all do you need to make a bank account

  • KYC details, name, address, PAN, ADDHAR
  • balance - money
In [9]:
%%file bank0.py

balance = 0

def get_balance():
    return balance

def deposit(amount):
    global balance
    balance = balance + amount
    
def withdraw(amount):
    global balance
    balance = balance - amount
    
    
if __name__ == "__main__":
    pass
Overwriting bank0.py
In [2]:
import bank0
In [3]:
bank0.get_balance()
Out[3]:
0
In [4]:
bank0.deposit(10000.0)
In [5]:
bank0.get_balance()
Out[5]:
10000.0
In [6]:
bank0.withdraw(2500)
In [7]:
bank0.get_balance()
Out[7]:
7500.0
In [10]:
%%file bank1.py

def make_account(KYC, initial_balance):
    return {"KYC":KYC, "balance":initial_balance}

def get_balance(account):
    return account['balance']

def deposit(account, amount):
    account['balance'] += amount
    
def withdraw(account, amount):
    account['balance'] -= amount
    
if __name__ == "__main__":
    pass
Writing bank1.py
In [11]:
import bank1
In [12]:
account1 = bank1.make_account("Vikrant", 5000)
In [13]:
bank1.deposit(account1, 700)
In [14]:
bank1.get_balance(account1)
Out[14]:
5700
In [15]:
bank1.withdraw(account1, 300)
In [16]:
bank1.get_balance(account1)
Out[16]:
5400
In [17]:
print("Vikrant's account balance:", bank1.get_balance(account1))
account2 = bank1.make_account("Alice", 4000)
bank1.withdraw(account2, 400)
print("Alice's account balance:", bank1.get_balance(account2))
print("Vikrant's account balance:", bank1.get_balance(account1))
Vikrant's account balance: 5400
Alice's account balance: 3600
Vikrant's account balance: 5400
            Instance of class
            +---------------+
            |               |<---------methods to manipulate data
            |    data       |
            |               |<---------methods
            +---------------+
                    |
                    |
                    +------<------- methods to access data
In [18]:
nums = [1, 2, 3, 4, 5, 6]
In [19]:
nums.append(0) # this is just one method from list which allows us to manipulate the data
In [20]:
class BankAccount:
    
    def __init__(self, KYC, initial_balance):
        self.KYC = KYC
        self.balance = initial_balance
        # there is no return statement required in __init__
        
    def get_balance(self):
        return self.balance
    
    
    def deposit(self, amount):
        self.balance += amount
        
    def withdraw(self, amount):
        self.balance -= amount
        
In [22]:
acc1 = BankAccount("Solo", 4000) # you call class name like a function...and it creates instance of class 
In [24]:
acc1 # this is instance object of BankAccount
Out[24]:
<__main__.BankAccount at 0x7f9f28153f70>
In [25]:
acc1.get_balance()
Out[25]:
4000
In [26]:
acc1.deposit(4500)
In [27]:
acc1.get_balance()
Out[27]:
8500
In [28]:
acc1.withdraw(567)
In [29]:
acc1.get_balance()
Out[29]:
7933
In [31]:
text = str("hello this is text!")
In [32]:
text.split()
Out[32]:
['hello', 'this', 'is', 'text!']
In [41]:
import random
class StockData:    
    
    def __init__(self, ticker):
        self.ticker = ticker
        
        
    def get_current_price(self):
        # fire complicated query on internet to any server that provides facility to give stock data
        return 100
    
    def get_last_week_daily_closing_prices(self):
         # fire complicated query on internet to any server that provides facility to give stock data
        return [random.random()*100 for i in range(7)]
    
    def buy(self, quantity):
        ## go to demat account, and place order
        
        return True
    
    
In [38]:
s = StockData("IBM")
s.get_current_price()
Out[38]:
100
In [39]:
s.get_last_week_daily_closing_prices()
Out[39]:
[95.80095646367658,
 26.711162046757096,
 71.21965347450825,
 85.66381897175262,
 76.501012700196,
 94.76433299078772,
 69.10920075057427]
In [40]:
s.buy(100)
Out[40]:
True
In [42]:
class BankAccount:
    
    def __init__(self, KYC, initial_balance):
        self.KYC = KYC
        self.balance = initial_balance
        # there is no return statement required in __init__
        
    def get_balance(self):
        return self.balance
    
    
    def deposit(self, amount):
        self.balance += amount
        
    def withdraw(self, amount):
        self.balance -= amount
        
In [43]:
a = BankAccount("KYC", 5000)
In [44]:
a.get_balance() # it expects one argument , self! but we are not passing it while calling
Out[44]:
5000
In [46]:
b = BankAccount("KYC", 5000)
In [47]:
a is b
Out[47]:
False
In [49]:
class BankAccount:
    
    def __init__(self, KYC, initial_balance):
        self.KYC = KYC
        self.balance = initial_balance
        # there is no return statement required in __init__
        
    def get_balance(self):
        return self.balance
    
    
    def deposit(self, amount):
        self.balance += amount
        
    def withdraw(self, amount):
        self.balance -= amount
        
    def __eq__(self, acc):
        return self.KYC == acc.KYC and self.balance == acc.balance
In [50]:
a1 = BankAccount("KYC", 5000)
a2 = BankAccount("KYC", 5000)
In [51]:
a1 == a2
Out[51]:
True
In [52]:
a1 is a2
Out[52]:
False
In [53]:
a1
Out[53]:
<__main__.BankAccount at 0x7f9f13e2c7f0>
In [54]:
print(a1)
<__main__.BankAccount object at 0x7f9f13e2c7f0>
In [60]:
class BankAccount:
    
    def __init__(self, KYC, initial_balance):
        self.KYC = KYC
        self.balance = initial_balance
        # there is no return statement required in __init__
        
    def get_balance(self):
        return self.balance
    
    
    def deposit(self, amount):
        self.balance += amount
        
    def withdraw(self, amount):
        self.balance -= amount
        
    def __eq__(self, acc):
        return self.KYC == acc.KYC and self.balance == acc.balance
    
    def __repr__(self):
        return "BankAccount<" + self.KYC + ">"
In [56]:
a =BankAccount("Alice", 5000)
In [57]:
a
Out[57]:
BankAccount<Alice>
In [58]:
nums = [1, 2, 34]
In [59]:
nums
Out[59]:
[1, 2, 34]

INHERITENCE¶

Have a look at modelling of Light as a class

            Light
            +---------------+
            |               |<---------switch_on
            |    on=True    |
            |               |<---------switch_off
            +---------------+
               |
               |
               +------<------- is_on
In [61]:
class Light:
    
    def __init__(self):
        self.on = False
        
    def switch_on(self):
        self.on = True
        
    def switch_off(self):
        self.on = False
        
    def is_on(self):
        return self.on
In [62]:
light1 = Light()
In [63]:
lights_from_house = [Light(), Light(), Light(), Light()]
In [70]:
def check_lights(lights):
    return [light.is_on() for light in lights]


def switch_off_all(lights):
    for light in lights:
        light.switch_off()
In [71]:
check_lights(lights_from_house)
Out[71]:
[False, False, False, False]
In [72]:
lights_from_house[-1].switch_on()
In [73]:
check_lights(lights_from_house)
Out[73]:
[False, False, False, True]
In [74]:
lights_from_house[1].switch_on()
In [75]:
check_lights(lights_from_house)
Out[75]:
[False, True, False, True]
In [76]:
switch_off_all(lights_from_house)
In [77]:
check_lights(lights_from_house)
Out[77]:
[False, False, False, False]
In [98]:
class Light:
    
    def __init__(self):
        self.on = False
        
    def switch_on(self):
        self.on = True
        
    def switch_off(self):
        self.on = False
        
    def is_on(self):
        return self.on

class ColoredLight(Light): # inheritence
    
    def __init__(self, color):
        self.color = color
        self.on = False
        
    def get_color(self):
        return self.color
    
    def change_color(self, color):
        self.color = color
In [92]:
c1 = ColoredLight("white")
In [93]:
c1.is_on()
Out[93]:
False
In [94]:
c1.switch_on()
In [95]:
c1.is_on()
Out[95]:
True
In [96]:
c1.get_color()
Out[96]:
'white'

Class Variables¶

In [99]:
class HDFCBankAccount(BankAccount):
    
    bankname = "HDFC"
In [100]:
alex = HDFCBankAccount("Alex", 6506)
In [101]:
alex.get_balance()
Out[101]:
6506
In [102]:
alex.bankname
Out[102]:
'HDFC'
In [103]:
ray = HDFCBankAccount("Ray", 4000)
In [104]:
ray.bankname
Out[104]:
'HDFC'
In [105]:
HDFCBankAccount.bankname = "NEWBANK"
In [106]:
ray.bankname
Out[106]:
'NEWBANK'
In [107]:
class DummyBankAccount(BankAccount):
    
    def set_bankname(self, name):
        self.bankname = name
In [108]:
db = DummyBankAccount("KYCsdsd", 4545)
In [113]:
db.set_bankname("SBI")
In [115]:
db.bankname
Out[115]:
'SBI'
In [111]:
db1 = DummyBankAccount("KYCsdsd", 4545)
In [114]:
db1.set_bankname("YES")
In [116]:
db1.bankname
Out[116]:
'YES'
In [136]:
class DummyBankAccount(BankAccount):
    
    bankname = "HDFC"
    
    def set_bankname(self, name):
        self.bankname = name # in real life programming we never use same names for class and instance variable
In [118]:
d1 = DummyBankAccount("safdsf", 565)
In [119]:
d1.bankname # this is class variable
Out[119]:
'HDFC'
In [121]:
d1.bankname
Out[121]:
'HDFC'
In [122]:
d2 = DummyBankAccount("dsds", 5676)
In [123]:
d2
Out[123]:
BankAccount<dsds>
In [124]:
d2.bankname
Out[124]:
'HDFC'
In [125]:
DummyBankAccount.bankname = "YES"
In [126]:
d1.bankname
Out[126]:
'YES'
In [127]:
d2.bankname
Out[127]:
'YES'
In [128]:
d1.set_bankname("SBI")
In [129]:
d1.bankname
Out[129]:
'SBI'
In [130]:
d2.bankname
Out[130]:
'YES'
In [131]:
d2.bankname
Out[131]:
'YES'
In [132]:
DummyBankAccount.bankname = "HELLO"
In [133]:
d2.bankname
Out[133]:
'HELLO'
In [135]:
d1.bankname # now it is referring to instance variable
Out[135]:
'SBI'
In [137]:
class FixedColorLight(Light):
    
    color = "Yellow"
    
In [143]:
class DefaultColoredLight(Light):
    
    def __init__(self, color="Red"): # this is default colur instance wise
        self.color = color
        super() # this calls parent __init__
In [140]:
DefaultColoredLight()
Out[140]:
<__main__.DefaultColoredLight at 0x7f9f284499f0>

Problem Write a class for Stock with fields name, value, high, low and mechanism to update value. Updating value will also update high and low automatically if required.

            Stock
            +---------------+
            |               |<---------update_value()
            |    fields     |
            |               |
            +---------------+
In [145]:
class Stock:
    
    def __init__(self, name, value):
        self.name = name
        self.value = value
        self.high = value
        self.low = value
        
    def update_value(self, value):
        self.value = value
        if value > self.high:
            self.high = value
        elif value < self.low:
            self.low = value
            
    def __repr__(self):
        return f"Stock<{self.name}, {self.value}>"  # f" jsagdj sajgd {localvar}  hdjhgsad {var2} sadas"  ..string formating
In [147]:
ibm = Stock("IBM", 500)
In [148]:
ibm
Out[148]:
Stock<IBM, 500>
In [149]:
ibm.update_value(600)
In [150]:
ibm
Out[150]:
Stock<IBM, 600>
In [151]:
ibm.update_value(300)
In [152]:
ibm
Out[152]:
Stock<IBM, 300>
In [153]:
ibm.high
Out[153]:
600
In [154]:
ibm.low
Out[154]:
300
In [ ]: