LAB 7 : Object Oriented Programming in Python

LAB 7 : Object Oriented Programming in Python

Object oriented Approach not only allows developers to reuse but with the help of polymorphism , inheritance and encapsulation it allows developers to easily read,manage,update, delete and fix bugs in the code.

Class in python

A class is a code ‘template’/’blueprint’ for creating objects on which functions are performed.It’s important to note that a class just provides structure—it’s a blueprint for how something should be defined, but it doesn’t actually provide any real content itself. 

class employee:
    pass

Object in Python

While the class is the blueprint, an instance is a copy of the class with actual values, literally an object belonging to a specific class.This example shows that two objects for class employee have been created which are unique and have unique locations in the memory, same is also verified emp1==emp2

class employee:
    pass
emp1 = employee()
emp2 = employee()
print(emp1)
print(emp2)
print(emp1==emp2)
<__main__.employee object at 0x00000278EB851160>
<__main__.employee object at 0x00000278EB8510F0>
False

Methods in Python

Methods are nothing but functions with classes. The only difference is methods automatically pass instance as its 1st argument which is called as self.

Initialization method or __init__() method in python

__init__ is a special method in Python classes, it is the constructor method for a class. In the following example you can see how to use it. __init__ is called when ever an object of the class is constructed.Basically __init__() is the first method which you will define after creating a class.All Instance variables or object variables are mentioned inside the __init__() method.

class employee:    
    def __init__(self,first,last,pay):
        self.first = first
        self.last = last
        self.pay = pay
        self.email = first + '.'+last+'@abc.com'
emp1 = employee('sameer','joshi',50000)
emp2 = employee('test','user',70000)    
print(emp1.first)
print(emp2.first)
sameer
test

User defined methods in Python

In the beow example we have created a method named fullname() this is called as user defined method.Note , self specifies that instance/object will be passed as first argument

class employee:
    def __init__(self,first,last,pay):
        self.first = first
        self.last = last
        self.pay = pay
        self.email = first + '.'+last+'@abc.com'
    def fullname(self):
        return '{} {}'.format(self.first,self.last)
emp1 = employee('sameer','joshi',50000)
emp2 = employee('ram','yadav',70000)     
print(emp2.fullname())
print(employee.fullname(emp1))
ram yadav
sameer joshi

Class Variables in python

Instance variable has to be unique for each instance/object but class variable have to be the same.Let us explain this with an example which will be common to all the employees that is raise or salary hike.

class employee:
    raise_amount=2    
    def __init__(self,first,last,pay):
        self.first = first
        self.last = last
        self.pay = pay
        self.email = first + '.'+last+'@abc.com'
    def fullname(self):
        return '{} {}'.format(self.first,self.last)
    def apply_raise(self):
        self.pay = int(self.pay*employee.raise_amount)
        return self.pay
emp1 = employee('sameer','joshi',50000)
emp2 = employee('ram','yadav',70000)     
print(emp1.pay)
print(emp1.apply_raise())
50000
100000

Example to find out total number of employees

class employee:
    num_of_emp=0
    
    def __init__(self,first,last,pay):
        self.first = first
        self.last = last
        self.pay = pay
        self.email = first + '.'+last+'@abc.com'
        employee.num_of_emp+=1

emp1 = employee('sameer','joshi',50000)
emp2 = employee('ram','yadav',70000)
emp3=employee('test','user',12345)

print(employee.num_of_emp)
3

Class Methods and Static Methods

Unlike other methods class method and static method are not bound to any object. So you don’t need any object or instance to call these methods.

Class Method

Uptil now we have seen that the regular methods automatically takes instances as 1st arguments which is called self. Similarly class methods take class as 1st argument which by convention is called cls.To create a class method you first have to declare that you are using the below method as class method and same is done by using a decorator called @classmethod.Classmethods can also be called by objects. It is not necessary to call class variable using class name only

class employee:
    raise_amount=2        
    def __init__(self,first,last,pay):
        self.first = first
        self.last = last
        self.pay = pay
        self.email = first + '.'+last+'@abc.com'        
    @classmethod
    def set_raise_amount(cls,amount):        
        cls.raise_amount = amount   
     
emp1 = employee('sameer','joshi',50000)
emp2 = employee('ram','yadav',70000)
employee.set_raise_amount(4)

print(employee.raise_amount)
print(emp1.raise_amount)
print(emp2.raise_amount)
4
4
4

Static method

We saw that regular methods pass instance automatically as first argument which is called self, class methods pass class automatically as first argument which is called cls but static methods don’t pass anything automatically. So static methods are similar to our functions but they are included within class and have some logical connection with the class. In order to tell python that given method is static method we need to declare it by using decorator @staticmethod. Let see one example

class employee:            
    def __init__(self,first,last,pay):
        self.first = first
        self.last = last
        self.pay = pay
        self.email = first + '.'+last+'@abc.com'        
    @staticmethod
    def is_workday(date):
        if date.weekday() == 5 or date.weekday() == 6:  
            return False
        else:
            return True        

import datetime
date = datetime.date(2020,7,21)
print('Is it a working day:',employee.is_workday(date))  
True

Inheritance

This allows us to inherit attributes and methods from parent class.This is useful as we can use the functionality of parent class in a subclass and then overwrite or add another functionality to it without affecting the parent class in any way.

class employee:            
    def __init__(self,first,last,pay):
        self.first = first
        self.last = last
        self.pay = pay
        self.email = first + '.'+last+'@abc.com'        
          
class developer(employee):
    pass

dev1 = developer('Divya','Shah',30000)
dev2 = developer('Nakul','Yadav',20000)

print(dev1.pay)
print(dev1.email)
30000
Divya.Shah@abc.com

In above example we have used all the attributes from parent class.Now lets us try to create some new attributes in child class.In below example e have created a new instance variable called prog_lang for developer class

class employee:            
    def __init__(self,first,last,pay):
        self.first = first
        self.last = last
        self.pay = pay
        self.email = first + '.'+last+'@abc.com'        
          
class developer(employee):
        def __init__(self,first,last,pay,prog_lang):
            self.prog_lang = prog_lang

dev1 = developer('Divya','Shah',30000,'Python')
dev2 = developer('Nakul','Yadav',20000,'Ansible')

print(dev1.prog_lang)
print(dev1.email)
Python
Traceback (most recent call last):
  File "C:/Users/Pallavi/AppData/Local/Programs/Python/Python36/my programs/oop.py", line 17, in <module>
    print(dev1.email)
AttributeError: 'developer' object has no attribute 'email'

you can see that we got correct output when tried to print(dev1.prog_lang) but when we tried to print(dev1.email) we got an error.This is because now developer class has its own __init__() method so it will not go to the parent class for instance variable.This can be avoided by telling child class that it has to refer the parent class for instance variable like firt,last and email

class employee:            
    def __init__(self,first,last,pay):
        self.first = first
        self.last = last
        self.pay = pay
        self.email = first + '.'+last+'@abc.com'        
          
class developer(employee):
        def __init__(self,first,last,pay,prog_lang):
            self.prog_lang = prog_lang
            super().__init__(first,last,pay)

dev1 = developer('Divya','Shah',30000,'Python')
dev2 = developer('Nakul','Yadav',20000,'Ansible')

print(dev1.prog_lang)
print(dev1.email)
Python
Divya.Shah@abc.com

Polymorphism

Polymorphism and Method Overriding. In literal sense, Polymorphism means the ability to take various forms. In PythonPolymorphism allows us to define methods in the child class with the same name as defined in their parent class.

class employee:            
    def __init__(self,first,last,pay):
        self.first = first
        self.last = last
        self.pay = pay
        self.email = first + '.'+last+'@abc.com'

    def fullname(self):
        return '{} {}'.format(self.first,self.last)
          
class developer(employee):
        def __init__(self,first,last,pay,prog_lang):
            self.prog_lang = prog_lang
            super().__init__(first,last,pay)

        def fullname(self):
            return 'my fullname is {} {} '.format(self.first,self.last)

emp1 = employee('Divya','Shah',30000)
dev1 =  developer('Nakul','Yadav',20000,'Ansible')

print(emp1.fullname())
print(dev1.fullname())
Divya Shah
my fullname is Nakul Yadav 

@property decorator in python OOP

Property decorators allows us to access methods as attributes.Means when object emp1 will access the method fullname he will not have to use parentheses. He can access it as attribute.

class employee:
       def __init__(self,first,last,pay):
        self.first = first
        self.last = last
        self.pay = pay        
    
    @property
    def fullname(self):
        return '{} {}'.format(self.first,self.last)

emp1 = employee('sam','roberts',50000)
print(emp1.fullname)
sam roberts

@setter in python oop

Now if we can access a method as an attribute then we should be able to provide a value to it as we do for any instance variable.This can be achieved using @setter.Same is shown in below code

class employee:   
    def __init__(self,first,last,pay):
        self.first = first
        self.last = last
        self.pay = pay       
    
    @property
    def fullname(self):
        return '{} {}'.format(self.first,self.last)

    @fullname.setter
    def fullname(self,name):
        first,last=name.split(' ')
        self.first=first
        self.last=last

emp1 = employee('sam','roberts',50000)
print(emp1.fullname)
emp1.fullname='Harry Potter'
print(emp1.fullname)
sam roberts
Harry Potter

Example : Replicate bank account to print the current balance as well as the amount deposited or withdrawn

class BankAccount:
    def __init__(self,name):
        
        self.name=name
        self.balance=0

    def withdraw(self, amount):
        print(self.name,'withdrawed:',amount)       
        self.balance -= amount
        print(self.name,'account balance is:',self.balance)        

    def deposit(self, amount):
        print(self.name,'Deposited:',amount)
        self.balance += amount
        print(self.name,'account balance is:',self.balance)       

accountholder1=BankAccount('John')
accountholder2=BankAccount('Peter')

accountholder1.deposit(100)
accountholder2.deposit(200)
accountholder1.withdraw(50)
accountholder2.withdraw(100)
John Deposited: 100
John account balance is: 100
Peter Deposited: 200
Peter account balance is: 200
John withdrawed: 50
John account balance is: 50
Peter withdrawed: 100
Peter account balance is: 100