22
OOP in Python
Requirement: List, Dictionary , Function etc
So, OOP is basically programming with Object
So, What is Object? To know this, you have to know what is Class?
Okay , do you know about string? Let's see what are there in the directory of string
print(dir(str))
Output:
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'removeprefix', 'removesuffix', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
so, what are they? There are methods under "str" class
for example: upper is a method and doc is a private method
and method is mainly function within a class . Nothing else!
So , you have a little knowledge of "class" now
Let's create a class named example :
class example:
print("Congratulations! you have created your first class")
Output:
Congratulations! you have created your first class
now we will create method within the class and remember that, while you create the method, you will have to give "self" parameter
class example:
def hello_world(self):
print("Created 1st method")
now the question is that what is self? this self is basically to differ from other class . But to know about this, you need to know what is object. You may consider object/instance as a child of a class. Let's create a class:
class example1:
def hello_world(self):
print("Created 1st method")
object=example1()# created an object
To use the class, you just need to create an object with the class name and you can then use all the method and variable within the class. Let's create an object and use it.
class example1:
def hello_world(self):
print("Hello world")
def details(self):
print("We are learning OOP")
object1=example1()
object1.details()#using details method
Output:
We are learning OOP
Here we have created an object and used details method of the class through the object
Now let's assume , you want to give some input and then work depending on those input, you can use init method for that . Basically init method is made to do must things of a class
class example1:
def __init__(self):
print("Hey! used the __init__ method")
def hello_world(self):
print("Hello world")
def details(self):
print("We are learning OOP")
object1=example1()
Output:
Hey! used the __init__ method
So, look here we created an object but did not call any method , still this is printed . The reason is that, we have added that under init . so these sort of things are done using init
.
Now, let's take some input while creating an object and use them .
class example1:
def __init__(self,name, country):
self.variable_1=name
self.variable_2=country
print(self.variable_1)
print(self.variable_2)
object1=example1("Mitul", 'Bangladesh')
here, while creating the object we are taking 2 values. Name and country name. and then look def init(self,name, country) here, we have set 3 parameter. self,name and country . Self is a must but other 2 are taken for 2 input we will take while creating an object.
class example1:
def __init__(self,name, country):
self.variable_1=name
self.variable_2=country
print(self.variable_1)
print(self.variable_2)
print("-----------")
object1=example1("Mitul", 'Bangladesh')
object2=example1('Karim', "India")
Now , you can see that we have 2 objects now and we can use as many time as we want providing name and country
Output:
Mitul
Bangladesh
-----------
Karim
India
-----------
You can again provide default values for variables within init
class example1:
def __init__(self,name, country="default"):
self.variable_1=name
self.variable_2=country
print(self.variable_1)
print(self.variable_2)
print("-----------")
object1=example1("Mitul")
Output:
Mitul
default
-----------
Now, let's know more about self using 2 class
#class 1
class Hospital:
def __init__(self,name):
self.name=name
self.d_dict={}
self.p_dict={}
self.d_count=0
self.p_count=0
def addDoctor(self,var):
self.d_dict[var.d_id]=[var.d_name,var.d_spe]
self.d_count+=1
def getDoctorByID(self,val):
if val in self.d_dict.keys():
return f"Doctor's ID:{val}\nName:{self.d_dict[val][0]}\nSpeciality:{self.d_dict[val][1]}"
def allDoctors(self):
print("All Doctors")
print(f"Number of Doctors: {self.d_count}")
print(self.d_dict)
#class 2
class Doctor:
def __init__(self,id,occ,name,spe):
self.d_id=id
self.d_occ=occ
self.d_name=name
self.d_spe=spe
h = Hospital("Evercare")# created an object with hospital name and Hospital class
d1 = Doctor("1d","Doctor", "Samar Kumar", "Neurologist") #created an object with Doctor class and with id, occupation , name , speciality
h.addDoctor(d1) #used a method of Hospital class . Notice h is not an object under Doctor class but addDoctor is under the class Doctor. So, we are basically using a method called addDoctor with an object not created from his own class
print(h.getDoctorByID("1d"))
Output:
Doctor's ID:1d
Name:Samar Kumar
Speciality:Neurologist
So, don't think about the code. Here we created "h" object through Hospital class and "d1" through Doctor class . so we used "getDoctorById" method through the object "h" and here into the code, while we used this:
def addDoctor(self,var):
self.d_dict[var.d_id]=[var.d_name,var.d_spe]
self.d_count+=1
if you check the line self.d_dict[var.d_id]=[var.d_name,var.d_spe] here, var refers to object of other class or in a word this is of a different class but self refers here things of only Hospital class. so , self here differs between 2 class .
So, stay cool. Don't need to panic if you don't realize anything.
Let's learn things gradually to master OOP
Public, Protected & Private Variable
Public variable : It can be used outside the class and in other class too
class Car:
numberOfWheels=4
class Bmw(Car):
def __init__(self):
print("Inside the BMW Class",self.numberOfWheels)
car=Car()
print(car.numberOfWheels)#used outside of the class
bmw=Bmw()
Output:
4
Inside the BMW Class 4
Protected Variable: This variable can be used in other class and also outside of the class but you need to use "_" to create this sort of variable
class Car:
_color = "Black" #proteced variable
class Bmw(Car):
def __init__(self):
print("Inside the BMW Class",self._color)#used within a different class
car=Car()
print(car._color)#used outside of the class
bmw=Bmw()
Output:
Black
Inside the BMW Class Black
Private Variable: You cannot use Private variable outside a class but use it within a class . Don't forget to use "__" before the variable
class Car:
__yearOfManufacture = 2017
def Private_key(self):
print("Private attribute yearOfManufacture: ",car.__yearOfManufacture) # private variable only works with its own class
car=Car()
car.Private_key()
Output:
Private attribute yearOfManufacture: 2017
Example using all of the variables
#Public=membername
#Protected=_memberName
#Private=__memberName
class Car:
numberOfWheels=4
_color="Black"
__yearOfManufacture=2017
def Private_key(self):
print("Private attribute yearOfManufacture: ", car.__yearOfManufacture) #private variable only works with its own class
class Bmw(Car):
def __init__(self):
print("Protected attribute color",self._color)#By using Inheritence we got Car class's variable
car=Car()
print("Public attribute numberOfWheels",car.numberOfWheels)
bmw=Bmw() # while we create this object . things under it's __init__ will be printed
car.Private_key()
Output:
Public attribute numberOfWheels 4
Protected attribute color Black
Private attribute yearOfManufacture: 2017
Class variable & Instance Variable
Instance Variable: Instance variable is variable dealing with the instance/object . You can change it's value outside of the class. You can access Instance variable by
class Book():
def __init__(self):
self.x = 100 # instance variable
def display(self):
print(self.x)
b = Book()
print(b.x) # printing instance variable
b.x=101 #changing the value of x
print(b.x)
Output:
100
101
Class Variable: Class Variable is valid for the class and can be called with its
class Book():
x = 5 # class variable
y=6
def __init__(self):
self.x = 100 # instance variable
def display(self):
print(self.x)
b = Book()
print("class variable",Book.x) # printing class variable
print("instance variable",b.x) # printing instance variable
print("class variable",Book.y)
#changng the class variable changes the value for class and instance
Book.y=7
print("class variable",Book.y)
print("instance variable",b.y)
Output:
class variable 5
instance variable 100
class variable 6
class variable 7
instance variable 7
Instance method, Class method ,Static method
Instance method: Instance method is method which can be used for instance/object .
class MyClass():
def __init__(self,x):
self._x=x
def method1(self):#instance method
print(self._x)
value=MyClass(100)
value.method1()
Output:
100
Class method: class method can be used by the class and you have to create @classmethod to create a class method and it can be accessed through . Again, you have to set cls as parameter of the class method
class MyClass():
a=5
#class method
@classmethod
def method2(cls): #cls refers to class object
print(cls.a)
MyClass.method2()#calling class method (prints 5)
Output:
5
But,if you don't want to use "cls", you can use your desired parameter name
class MyClass():
a=5
#class method
@classmethod
def method2(class_method): #cls refers to class object
print(class_method.a)
MyClass.method2()#calling class method (prints 5)
Output:
5
Static Method: Static method does not have any must parameter like self or cls . It just works like a random function we used to make
class MyClass():
@staticmethod
def method3(m,n): #takes 2 value
return m+n #returns their sum
object=MyClass()
print(object.method3(10,20))
Output:
30
property method
To get value from a method, you may set it as a property method using @property before the method name . You can access the property method using . Don't use () at the end of the method name .
class Product:
def __init__(self, x, y):
self._x = x
self._y = y
@property # to get any value using this method written as object.method or, p.value not like p.value()
def value(self): #property method
return self._x
p = Product(23, 24)
print(p.value) # you cannot use p.value()
Output:
23
Again you can set value of the property method by using
class Product:
def __init__(self, x, y):
self._x = x
self._y = y
@property # to get any value using this method written as object.method or, p.value not like p.value()
def value(self):
return self._x
@value.setter # to set value
def value(self, val):
self._x=val
p = Product(23, 24)
print(p.value)
p.value=100
print("After setting the new value, it is now",p.value)
Output:
23
After setting the new value, it is now 100
To delete a value from property method, you can use
class Product:
def __init__(self, x, y):
self._x = x
self._y = y
@property # to get any value using this method written a variable ex: object.method or, p.value not like p.value()
def value(self):
return self._x
@value.setter # to set a function to assign value
def value(self, val):
self._x = val
# while we delete a value,this method will be applied ex: del p.value
@value.deleter
def value(self):
print('Value deleted')
p = Product(12, 24)
print("Property object has the 1st value",p.value)
#to delete the value use del and then objectname.variable name
del p.value
Output:
Property object has the 1st value 12
Value deleted
Dispatch method
Dispatch method is used to work with specific thing . for example if you want to work with 3 integers or 3 float or float and an integer, you can create a custom method.
Note: Don't forget to install multipledispatch package
for example to work with 3 integers, you can use and then your desired method with 4 parameters including self .
@dispatch(int,int,int)#working for 3 integers
def product(self,a,b,c):
Let's check a code
from multipledispatch import dispatch
class my_calculator():
@dispatch(int,int)#when we have 2 input, it will work
def product(self,a,b):
print("Product of 2 integers : ",a*b)
@dispatch(int,int,int)#working for 3 integers
def product(self,a,b,c):
print("Product of 3 integers : ",a*b)
@dispatch(float,float,float)#working for 3 floats
def product(self,a,b,c):
print("Product of 3 floats : ",a*b*c)
@dispatch(float,int)#working for a int and a float
def product(self,c,d):
print("Product of 1 float and 1 integer : ",c*d)
c1=my_calculator()
c1.product(4,5)
c1.product(4,7,6)
c1.product(4.0,5.0,3.0)
c1.product(4.0,3)
Output:
Product of 2 integers : 20
Product of 3 integers : 28
Product of 3 floats : 60.0
Product of 1 float and 1 integer : 12.0
Magic Method
Magic method starts with __ and ends with __
Code:
class Fraction:
def __init__(self,nr,dr=1):
self.nr=nr
self.dr=dr
if self.dr<0:
self.nr*=-1
self.dr*=-1
self.__reduce__()
def show(self):
print(f'{self.nr}/{self.dr}')
def __str__(self):
return f'{self.nr}/{self.dr}'
def __repr__(self):
return f'Fraction({self.nr}/{self.dr})'
def __add__(self, other):#magic method __method__
if isinstance(other,int):
other=Fraction(other)
f=Fraction(self.nr*other.dr+other.nr*self.dr)
f.__reduce__()
return f
def __radd__(self, other):#reverse add
return self.__add__(other)
def __sub__(self, other):
if isinstance(other,int):
other=Fraction(other)
f=Fraction(self.nr*other.dr-other.nr*self.dr)
f.__reduce__()
return f
def __mult__(self,other):
if isinstance(other,int):
other=Fraction(other)
f=Fraction(self.nr*other.nr,self.dr*other.dr)
f.__reduce__()
return f
def __eq__(self,other):
return (self.nr*other.dr)==(self.dr*other.nr)
def __lt__(self, other):
return (self.nr*other.dr)<(self.dr*other.nr)
def __le__(self, other):
return (self.nr*other.dr)<=(self.dr*other.nr)
def __reduce__(self):
h=Fraction.hcf(self.nr,self.dr)
if h==0:
return
self.nr//=h
self.dr//=h
@staticmethod
def something(): pass
Protected method , Private method
You can use protected method outside of the class but cannot use private method outside of the method
class Product:
def __init__(self):
self.data1=10
self._data2=20 #protected variable
def methodA(self):
pass
def _methodB(self): #pprotected method
print("Hello to protected method")
def __methodC(self):#private method
print("Hola")
p=Product()
print(dir(p)) #You can see additionally _data2', '_methodB', 'data1', 'methodA'
print(p._data2) #accessing the protected variable
p._methodB() #calling the proteced method
Output:
['_Product__methodC', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_data2', '_methodB', 'data1', 'methodA']
20
Hello to protected method
Operator Overloading
To set specific rules for a specific operator, we use operator overloading . For example, add is used for "+"
Check out this link: https://www.geeksforgeeks.org/operator-overloading-in-python/
If we want to add 2 different object one has 1 and 6 and other has 9 and 3, we will use operator overloading to do so
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __str__(self): #if asked to print string type
return "({0},{1})".format(self.x, self.y)
def __add__(self, other): #works for + opetator
x = self.x + self.y
y = other.x + other.y
return Point(x, y)
p1 = Point(1, 6)#worked for self
p2 = Point(9, 3)#other
print("P1 has 1st value",p1.x)
print("P1 has 2nd value", p1.y)
print("P2 has 1st value",p2.x)
print("P2 has 2nd value", p2.y)
print("Summation of p1+p2 is",p1+p2)#as p1 is first so self is for p1 and p2 gets others
To realize it in a better way,use Thonny IDE from this link (https://thonny.org/) and paste this code and debug . You can see how the code is proceeding
Ouput:
P1 has 1st value 1
P1 has 2nd value 6
P2 has 1st value 9
P2 has 2nd value 3
Summation of p1+p2 is (7,12)
Process finished with exit code 0
Polymorphism
There might be method of same name but to use them depending on their class, we use it like this
class Car:
def start(self):
print('Engine started')
def move(self):
print('Car is running')
def stop(self):
print('Brales applied')
class Clock:
def move(self):
print('Tick Tick Tick')
def stop(self):
print('Clock needles stopped')
class Person:
def move(self):
print('Person walking')
def stop(self):
print('Taking rest')
def talk(self):
print('Hello')
car=Car()
clock=Clock()
person=Person()
#this method will run with which instance you call it
def do_something(x):
x.move()
x.stop()
# calling with car instance
do_something(car) #car is an object of Car class
#calling with clock instance
do_something(clock) # clock is an object of Clock class
#calling with person instance
do_something(person) #person is an object of Person class
Output:
Car is running
Brales applied
Tick Tick Tick
Clock needles stopped
Person walking
Taking rest
Inheritance
Let's assume that your college has CSE , BBA department . They have few things in common. All of them have student ID card, they are from he same college . So,, while you want to take all the information of BBA student or CSE student , you can do one thing. You can create a class names Student which works for common purposes and you can create 2 different class which will work with other extra information like BBA Students with have marketing classes where CSE Students will have Labs. So, to work with this code , we can use inheritance . So, while creating BBA Student class , we will use the "Student" class in the peremeter to mean inheritance
Note: Here "Student": class will be called parent class and "BBA Student " class will be student class
Again , to use something from the parent class, you will have to use
class Student:
def __init__(self, name='Just a student', dept='nothing'):
self.__name = name
self.__department = dept
def set_department(self, dept):
self.__department = dept
def get_name(self):
return self.__name
def set_name(self, name):
self.__name = name
def __str__(self):
return f"Name: {self.__name} \nDepartment: {self.__department}\n"
# write your code here
class BBA_Student(Student):
def __init__(self, name="default", department="BBA"):
super().__init__(name, department)#used Student class's __init__ method
print("I am a BBA Students . We do marketing courses")
class CSE_Student(Student):
def __init__(self,name="default",department="CSE"):
super().__init__(name,department)#used Student class's __init__ method
print("I am a CSE Student and I have a lots of lab to complete")
print(BBA_Student('Karim Ali'))#using BBA_Student class inherited Student class
print(CSE_Student('Mitul'))# using CSE_Student class inherited Student class
Output:
I am a BBA Students . We do marketing courses
Name: Karim Ali
Department: BBA
I am a CSE Student and I have a lots of lab to complete
Name: Mitul
Department: CSE
22