0% found this document useful (0 votes)
7 views55 pages

Unit V Class

This document provides an overview of Object-Oriented Programming (OOP) in Python, explaining key concepts such as classes, objects, attributes, and methods. It covers the creation of classes, instantiation of objects, and the differences between instance variables and class variables, as well as inheritance types. Additionally, it discusses naming conventions, modifying and deleting object properties, and the use of the pass statement in class definitions.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
7 views55 pages

Unit V Class

This document provides an overview of Object-Oriented Programming (OOP) in Python, explaining key concepts such as classes, objects, attributes, and methods. It covers the creation of classes, instantiation of objects, and the differences between instance variables and class variables, as well as inheritance types. Additionally, it discusses naming conventions, modifying and deleting object properties, and the use of the pass statement in class definitions.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 55

UNIT V OBJECT ORIENTED PROGRAMMING IN PYTHON

What is a Class and Objects in Python?


 Class: The class is a user-defined data structure that binds the data members
and methods into a single unit. Class is a blueprint or code template for
object creation. Using a class, you can create as many objects as you want.
 Object: An object is an instance of a class. It is a collection of attributes
(variables) and methods. We use the object of a class to perform actions.
Objects have two characteristics: They have states and behaviors (object has attributes
and methods attached to it) Attributes represent its state, and methods represent its
behavior. Using its methods, we can modify its state.
In short, Every object has the following property.
 Identity: Every object must be uniquely identified.
 State: An object has an attribute that represents a state of an object, and it also
reflects the property of an object.
 Behavior: An object has methods that represent its behavior.
Python is an Object-Oriented Programming language, so everything in Python is
treated as an object. An object is a real-life entity. It is the collection of various data
and functions that operate on those data.
For example, If we design a class based on the states and behaviors of a Person, then
States can be represented as instance variables and behaviors as class methods.
A real-life example of class and objects.
Class: Person
 State: Name, Sex, Profession
 Behavior: Working, Study
Using the above class, we can create multiple objects that depict different states and
behavior.
Object 1: Jessa
 State:
 Name: Jessa
 Sex: Female
 Profession: Software Engineer
Behavior:
 Working: She is working as a software developer at ABC Company
 Study: She studies 2 hours a day
Object 2: Jon
 State:
 Name: Jon
 Sex: Male
 Profession: Doctor
Behavior:
 Working: He is working as a doctor
 Study: He studies 5 hours a day
As you can see, Jessa is female, and she works as a Software engineer. On the other
hand, Jon is a male, and he is a lawyer. Here, both objects are created from the same
class, but they have different states and behaviors.
Create a Class in Python
In Python, class is defined by using the class keyword. The syntax to create a class is
given below.
Syntax

class class_name:
'''This is a docstring. I have created a new class'''
<statement 1>
<statement 2>
.
.
<statement N>

 class_name: It is the name of the class


 Docstring: It is the first string inside the class and has a brief description of the
class. Although not mandatory, this is highly recommended.
 statements: Attributes and methods
Example: Define a class in Python
In this example, we are creating a Person Class with name, sex, and profession
instance variables.

class Person:
def __init__(self, name, sex, profession):
# data members (instance variables)
self.name = name
self.sex = sex
self.profession = profession

# Behavior (instance methods)


def show(self):
print('Name:', self.name, 'Sex:', self.sex, 'Profession:', self.profession)

# Behavior (instance methods)


def work(self):
print(self.name, 'working as a', self.profession)

Create Object of a Class


An object is essential to work with the class attributes. The object is created using the
class name. When we create an object of the class, it is called instantiation. The object
is also called the instance of a class.
A constructor is a special method used to create and initialize an object of a class. This
method is defined in the class.
In Python, Object creation is divided into two parts in Object Creation and Object
initialization
 Internally, the __new__ is the method that creates the object
 And, using the __init__() method we can implement constructor to initialize
the object.
Syntax
<object-name> = <class-name>(<arguments>)

Below is the code to create the object of a Person class

jessa = Person('Jessa', 'Female', 'Software Engineer')

The complete example:

class Person:
def __init__(self, name, sex, profession):
# data members (instance variables)
self.name = name
self.sex = sex
self.profession = profession

# Behavior (instance methods)


def show(self):
print('Name:', self.name, 'Sex:', self.sex, 'Profession:', self.profession)

# Behavior (instance methods)


def work(self):
print(self.name, 'working as a', self.profession)

# create object of a class


jessa = Person('Jessa', 'Female', 'Software Engineer')

# call methods
jessa.show()
jessa.work()

Run
Output:
Name: Jessa Sex: Female Profession: Software Engineer
Jessa working as a Software Engineer

Class Attributes
When we design a class, we use instance variables and class variables.
In Class, attributes can be defined into two parts:
 Instance variables: The instance variables are attributes attached to an
instance of a class. We define instance variables in the constructor (
the __init__() method of a class).
 Class Variables: A class variable is a variable that is declared inside of class,
but outside of any instance method or __init__() method.
Class Attributes in Python
Objects do not share instance attributes. Instead, every object has its copy of the
instance attribute and is unique to each object.
All instances of a class share the class variables. However, unlike instance variables,
the value of a class variable is not varied from object to object.
Only one copy of the static variable will be created and shared between all objects of
the class.
Accessing properties and assigning values
 An instance attribute can be accessed or modified by using the dot
notation: instance_name.attribute_name.
 A class variable is accessed or modified using the class name
Example

class Student:
# class variables
school_name = 'ABC School'

# constructor
def __init__(self, name, age):
# instance variables
self.name = name
self.age = age

s1 = Student("Harry", 12)
# access instance variables
print('Student:', s1.name, s1.age)

# access class variable


print('School name:', Student.school_name)

# Modify instance variables


s1.name = 'Jessa'
s1.age = 14
print('Student:', s1.name, s1.age)

# Modify class variables


Student.school_name = 'XYZ School'
print('School name:', Student.school_name)

Run
Output

Student: Harry 12
School name: ABC School

Student: Jessa 14
School name: XYZ School

Class Methods
In Object-oriented programming, Inside a Class, we can define the following three
types of methods.
 Instance method: Used to access or modify the object state. If we use instance
variables inside a method, such methods are called instance methods.
 Class method: Used to access or modify the class state. In method
implementation, if we use only class variables, then such type of methods we
should declare as a class method.
 Static method: It is a general utility method that performs a task in isolation.
Inside this method, we don’t use instance or class variable because this static
method doesn’t have access to the class attributes.

class method vs static method vs instance method


Instance methods work on the instance level (object level). For example, if we have
two objects created from the student class, They may have different names, marks, roll
numbers, etc. Using instance methods, we can access and modify the instance
variables.
A class method is bound to the class and not the object of the class. It can access
only class variables.
Example: Define and call an instance method and class method

# class methods demo


class Student:
# class variable
school_name = 'ABC School'

# constructor
def __init__(self, name, age):
# instance variables
self.name = name
self.age = age

# instance method
def show(self):
# access instance variables and class variables
print('Student:', self.name, self.age, Student.school_name)

# instance method
def change_age(self, new_age):
# modify instance variable
self.age = new_age

# class method
@classmethod
def modify_school_name(cls, new_name):
# modify class variable
cls.school_name = new_name

s1 = Student("Harry", 12)

# call instance methods


s1.show()
s1.change_age(14)
# call class method
Student.modify_school_name('XYZ School')
# call instance methods
s1.show()

Run
Output

Student: Harry 12 ABC School


Student: Harry 14 XYZ School

Class Naming Convention


Naming conventions are essential in any programming language for better readability.
If we give a sensible name, it will save our time and energy later. Writing readable
code is one of the guiding principles of the Python language.
We should follow specific rules while we are deciding a name for the class in Python.
 Rule-1: Class names should follow the UpperCaseCamelCase convention
 Rule-2: Exception classes should end in “Error“.
 Rule-3: If a class is callable (Calling the class from somewhere), in that case,
we can give a class name like a function.
 Rule-4: Python’s built-in classes are typically lowercase words
pass Statement in Class
In Python, the pass is a null statement. Therefore, nothing happens when the pass
statement is executed.
The pass statement is used to have an empty block in a code because the empty code is
not allowed in loops, function definition, class definition. Thus, the pass statement
will results in no operation (NOP). Generally, we use it as a placeholder when we do
not know what code to write or add code in a future release.
For example, suppose we have a class that is not implemented yet, but we want to
implement it in the future, and they cannot have an empty body because the interpreter
gives an error. So use the pass statement to construct a body that does nothing.
Example

class Demo:
pass

In the above example, we defined class without a body. To avoid errors while
executing it, we added the pass statement in the class body.
Object Properties
Every object has properties with it. In other words, we can say that object property is
an association between name and value.
For example, a car is an object, and its properties are car color, sunroof, price,
manufacture, model, engine, and so on. Here, color is the name and red is the value.
Object properties are nothing but instance variables.

Object Properties
Modify Object Properties
Every object has properties associated with them. We can set or modify the object’s
properties after object initialization by calling the property directly using the dot
operator.
Obj.PROPERTY = value

Run
Example

class Fruit:
def __init__(self, name, color):
self.name = name
self.color = color

def show(self):
print("Fruit is", self.name, "and Color is", self.color)

# creating object of the class


obj = Fruit("Apple", "red")

# Modifying Object Properties


obj.name = "strawberry"

# calling the instance method using the object obj


obj.show()
# Output Fruit is strawberry and Color is red

Run
Delete object properties
We can delete the object property by using the del keyword. After deleting it, if we try
to access it, we will get an error.

class Fruit:
def __init__(self, name, color):
self.name = name
self.color = color
def show(self):
print("Fruit is", self.name, "and Color is", self.color)

# creating object of the class


obj = Fruit("Apple", "red")

# Deleting Object Properties


del obj.name

# Accessing object properties after deleting


print(obj.name)
# Output: AttributeError: 'Fruit' object has no attribute 'name'

Run
In the above example, As we can see, the attribute name has been deleted when we try
to print or access that attribute gets an error message.
Delete Objects
In Python, we can also delete the object by using a del keyword. An object can be
anything like, class object, list, tuple, set, etc.
Syntax

del object_name

Example: Deleting object

class Employee:
depatment = "IT"

def show(self):
print("Department is ", self.depatment)
emp = Employee()
emp.show()

# delete object
del emp

# Accessing after delete object


emp.show()
# Output : NameError: name 'emp' is not defined

Run
In the above example, we create the object emp of the class Employee. After that,
using the del keyword, we deleted that object.
Inheritance in Python
The process of inheriting the properties of the parent class into a child class is
called inheritance. The existing class is called a base class or parent class and the
new class is called a subclass or child class or derived class.
In this Python lesson, you will learn inheritance, method overloading, method
overriding, types of inheritance, and MRO (Method Resolution Order).
In Object-oriented programming, inheritance is an important aspect. The main purpose
of inheritance is the reusability of code because we can use the existing class to create
a new class instead of creating it from scratch.
In inheritance, the child class acquires all the data members, properties, and functions
from the parent class. Also, a child class can also provide its specific implementation
to the methods of the parent class.
For example, In the real world, Car is a sub-class of a Vehicle class. We can create a
Car by inheriting the properties of a Vehicle such as Wheels, Colors, Fuel tank,
engine, and add extra properties in Car as required.
Syntax

class BaseClass:
Body of base class
class DerivedClass(BaseClass):
Body of derived class

Types Of Inheritance
In Python, based upon the number of child and parent classes involved, there are five
types of inheritance. The type of inheritance are listed below:
1. Single inheritance
2. Multiple Inheritance
3. Multilevel inheritance
4. Hierarchical Inheritance
5. Hybrid Inheritance
Now let’s see each in detail with an example.
Single Inheritance
In single inheritance, a child class inherits from a single-parent class. Here is one child
class and one parent class.

Python Single Inheritance


Example
Let’s create one parent class called ClassOne and one child class called ClassTwo to
implement single inheritance.

# Base class
class Vehicle:
def Vehicle_info(self):
print('Inside Vehicle class')
# Child class
class Car(Vehicle):
def car_info(self):
print('Inside Car class')

# Create object of Car


car = Car()

# access Vehicle's info using car object


car.Vehicle_info()
car.car_info()

Output

Inside Vehicle class


Inside Car class

Multiple Inheritance
In multiple inheritance, one child class can inherit from multiple parent classes. So
here is one child class and multiple parent classes.

Python Multiple Inheritance


Example

# Parent class 1
class Person:
def person_info(self, name, age):
print('Inside Person class')
print('Name:', name, 'Age:', age)

# Parent class 2
class Company:
def company_info(self, company_name, location):
print('Inside Company class')
print('Name:', company_name, 'location:', location)

# Child class
class Employee(Person, Company):
def Employee_info(self, salary, skill):
print('Inside Employee class')
print('Salary:', salary, 'Skill:', skill)

# Create object of Employee


emp = Employee()

# access data
emp.person_info('Jessa', 28)
emp.company_info('Google', 'Atlanta')
emp.Employee_info(12000, 'Machine Learning')

Output

Inside Person class


Name: Jessa Age: 28

Inside Company class


Name: Google location: Atlanta
Inside Employee class
Salary: 12000 Skill: Machine Learning

In the above example, we created two parent


classes Person and Company respectively. Then we create one child
called Employee which inherit from Person and Company classes.
Multilevel inheritance
In multilevel inheritance, a class inherits from a child class or derived class. Suppose
three classes A, B, C. A is the superclass, B is the child class of A, C is the child class
of B. In other words, we can say a chain of classes is called multilevel inheritance.

Python Multilevel Inheritance


Example

# Base class
class Vehicle:
def Vehicle_info(self):
print('Inside Vehicle class')

# Child class
class Car(Vehicle):
def car_info(self):
print('Inside Car class')
# Child class
class SportsCar(Car):
def sports_car_info(self):
print('Inside SportsCar class')

# Create object of SportsCar


s_car = SportsCar()

# access Vehicle's and Car info using SportsCar object


s_car.Vehicle_info()
s_car.car_info()
s_car.sports_car_info()

Output

Inside Vehicle class


Inside Car class
Inside SportsCar class

In the above example, we can see there are three classes


named Vehicle, Car, SportsCar. Vehicle is the superclass, Car is a child of
Vehicle, SportsCar is a child of Car. So we can see the chaining of classes.
Hierarchical Inheritance
In Hierarchical inheritance, more than one child class is derived from a single parent
class. In other words, we can say one parent class and multiple child classes.
Python hierarchical inheritance
Example
Let’s create ‘Vehicle’ as a parent class and two child class ‘Car’ and ‘Truck’ as a
parent class.

class Vehicle:
def info(self):
print("This is Vehicle")

class Car(Vehicle):
def car_info(self, name):
print("Car name is:", name)

class Truck(Vehicle):
def truck_info(self, name):
print("Truck name is:", name)

obj1 = Car()
obj1.info()
obj1.car_info('BMW')

obj2 = Truck()
obj2.info()
obj2.truck_info('Ford')
Output

This is Vehicle
Car name is: BMW

This is Vehicle
Truck name is: Ford

Hybrid Inheritance
When inheritance is consists of multiple types or a combination of different
inheritance is called hybrid inheritance.

Python hybrid inheritance


Example

class Vehicle:
def vehicle_info(self):
print("Inside Vehicle class")

class Car(Vehicle):
def car_info(self):
print("Inside Car class")

class Truck(Vehicle):
def truck_info(self):
print("Inside Truck class")

# Sports Car can inherits properties of Vehicle and Car


class SportsCar(Car, Vehicle):
def sports_car_info(self):
print("Inside SportsCar class")

# create object
s_car = SportsCar()

s_car.vehicle_info()
s_car.car_info()
s_car.sports_car_info()

Note: In the above example, hierarchical and multiple inheritance exists. Here we
created, parent class Vehicle and two child classes named Car and Truck this is
hierarchical inheritance.
Another is SportsCar inherit from two parent classes named Car and Vehicle. This is
multiple inheritance.
Python super() function
When a class inherits all properties and behavior from the parent class is called
inheritance. In such a case, the inherited class is a subclass and the latter class is the
parent class.
In child class, we can refer to parent class by using the super() function. The super
function returns a temporary object of the parent class that allows us to call a parent
class method inside a child class method.
Benefits of using the super() function.
1. We are not required to remember or specify the parent class name to access its
methods.
2. We can use the super() function in both single and multiple inheritances.
3. The super() function support code reusability as there is no need to write the
entire function
Example

class Company:
def company_name(self):
return 'Google'

class Employee(Company):
def info(self):
# Calling the superclass method using super()function
c_name = super().company_name()
print("Jessa works at", c_name)

# Creating object of child class


emp = Employee()
emp.info()

Output:

Jessa works at Google

In the above example, we create a parent class Company and child class Employee.
In Employee class, we call the parent class method by using a super() function.
issubclass()
In Python, we can verify whether a particular class is a subclass of another class. For
this purpose, we can use Python built-in function issubclass(). This function
returns True if the given class is the subclass of the specified class. Otherwise, it
returns False.
Syntax

issubclass(class, classinfo)
Where,
 class: class to be checked.
 classinfo: a class, type, or a tuple of classes or data types.
Example

class Company:
def fun1(self):
print("Inside parent class")

class Employee(Company):
def fun2(self):
print("Inside child class.")

class Player:
def fun3(self):
print("Inside Player class.")

# Result True
print(issubclass(Employee, Company))

# Result False
print(issubclass(Employee, list))

# Result False
print(issubclass(Player, Company))

# Result True
print(issubclass(Employee, (list, Company)))

# Result True
print(issubclass(Company, (list, Company)))
Method Overriding
In inheritance, all members available in the parent class are by default available in the
child class. If the child class does not satisfy with parent class implementation, then
the child class is allowed to redefine that method by extending additional functions in
the child class. This concept is called method overriding.
When a child class method has the same name, same parameters, and same return type
as a method in its superclass, then the method in the child is said to override the
method in the parent class.

Python method overriding


Example

class Vehicle:
def max_speed(self):
print("max speed is 100 Km/Hour")

class Car(Vehicle):
# overridden the implementation of Vehicle class
def max_speed(self):
print("max speed is 200 Km/Hour")

# Creating object of Car class


car = Car()
car.max_speed()

Output:

max speed is 200 Km/Hour

In the above example, we create two classes named Vehicle (Parent class)
and Car (Child class). The class Car extends from the class Vehicle so, all properties
of the parent class are available in the child class. In addition to that, the child class
redefined the method max_speed().
Method Resolution Order in Python
In Python, Method Resolution Order(MRO) is the order by which Python looks for a
method or attribute. First, the method or attribute is searched within a class, and then
it follows the order we specified while inheriting.
This order is also called the Linearization of a class, and a set of rules is called MRO
(Method Resolution Order). The MRO plays an essential role in multiple
inheritances as a single method may found in multiple parent classes.
In multiple inheritance, the following search order is followed.
1. First, it searches in the current parent class if not available, then searches in the
parents class specified while inheriting (that is left to right.)
2. We can get the MRO of a class. For this purpose, we can use either
the mro attribute or the mro() method.
Example

class A:
def process(self):
print(" In class A")

class B(A):
def process(self):
print(" In class B")
class C(B, A):
def process(self):
print(" In class C")

# Creating object of C class


C1 = C()
C1.process()
print(C.mro())
# In class C
# [<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class
'object'>]

In the above example, we create three classes named A, B and C. Class B is inherited
from A, class C inherits from B and A. When we create an object of the C class and
calling the process() method, Python looks for the process() method in the current
class in the C class itself.
Then search for parent classes, namely B and A, because C class inherit from B and A.
that is, C(B, A) and always search in left to right manner.
What is Polymorphism in Python?
Polymorphism in Python is the ability of an object to take many forms. In simple
words, polymorphism allows us to perform the same action in many different ways.
For example, Jessa acts as an employee when she is at the office. However, when she
is at home, she acts like a wife. Also, she represents herself differently in different
places. Therefore, the same person takes different forms as per the situation.
A person takes different forms
In polymorphism, a method can process objects differently depending on the class
type or data type. Let’s see simple examples to understand it better.
Polymorphism in Built-in function len()
The built-in function len() calculates the length of an object depending upon its type.
If an object is a string, it returns the count of characters, and If an object is a list, it
returns the count of items in a list.
The len() method treats an object as per its class type.
Example:

students = ['Emma', 'Jessa', 'Kelly']


school = 'ABC School'

# calculate count
print(len(students))
print(len(school))

Output

3
10
Polymorphic len() function
Polymorphism With Inheritance
Polymorphism is mainly used with inheritance. In inheritance, child class inherits the
attributes and methods of a parent class. The existing class is called a base class or
parent class, and the new class is called a subclass or child class or derived class.
Using method overriding polymorphism allows us to defines methods in the
child class that have the same name as the methods in the parent class. This process of
re-implementing the inherited method in the child class is known as Method
Overriding.
Advantage of method overriding
 It is effective when we want to extend the functionality by altering the
inherited method. Or the method inherited from the parent class doesn’t
fulfill the need of a child class, so we need to re-implement the same method
in the child class in a different way.
 Method overriding is useful when a parent class has multiple child classes, and
one of that child class wants to redefine the method. The other child classes
can use the parent class method. Due to this, we don’t need to modification
the parent class code
In polymorphism, Python first checks the object’s class type and executes the
appropriate method when we call the method. For example, If you create the Car
object, then Python calls the speed() method from a Car class.
Let’s see how it works with the help of an example.
Example: Method Overriding
In this example, we have a vehicle class as a parent and a ‘Car’ and ‘Truck’ as its sub-
class. But each vehicle can have a different seating capacity, speed, etc., so we can
have the same instance method name in each class but with a different
implementation. Using this code can be extended and easily maintained over time.

Polymorphism with Inheritance

class Vehicle:

def __init__(self, name, color, price):


self.name = name
self.color = color
self.price = price

def show(self):
print('Details:', self.name, self.color, self.price)

def max_speed(self):
print('Vehicle max speed is 150')

def change_gear(self):
print('Vehicle change 6 gear')

# inherit from vehicle class


class Car(Vehicle):
def max_speed(self):
print('Car max speed is 240')

def change_gear(self):
print('Car change 7 gear')

# Car Object
car = Car('Car x1', 'Red', 20000)
car.show()
# calls methods from Car class
car.max_speed()
car.change_gear()

# Vehicle Object
vehicle = Vehicle('Truck x1', 'white', 75000)
vehicle.show()
# calls method from a Vehicle class
vehicle.max_speed()
vehicle.change_gear()

Output:
Details: Car x1 Red 20000
Car max speed is 240
Car change 7 gear

Details: Truck x1 white 75000


Vehicle max speed is 150
Vehicle change 6 gear

As you can see, due to polymorphism, the Python interpreter recognizes that
the max_speed() and change_gear() methods are overridden for the car object. So, it
uses the one defined in the child class (Car)
On the other hand, the show() method isn’t overridden in the Car class, so it is used
from the Vehicle class.
Overrride Built-in Functions
In Python, we can change the default behavior of the built-in functions. For example,
we can change or extend the built-in functions such as len(), abs(), or divmod() by
redefining them in our class. Let’s see the example.
Example
In this example, we will redefine the function len()

class Shopping:
def __init__(self, basket, buyer):
self.basket = list(basket)
self.buyer = buyer

def __len__(self):
print('Redefine length')
count = len(self.basket)
# count total items in a different way
# pair of shoes and shir+pant
return count * 2
shopping = Shopping(['Shoes', 'dress'], 'Jessa')
print(len(shopping))

Output

Redefine length
4

Polymorphism In Class methods


Polymorphism with class methods is useful when we group different objects having
the same method. we can add them to a list or a tuple, and we don’t need to check the
object type before calling their methods. Instead, Python will check object type at
runtime and call the correct method. Thus, we can call the methods without being
concerned about which class type each object is. We assume that these methods exist
in each class.
Python allows different classes to have methods with the same name.
 Let’s design a different class in the same way by adding the same methods in
two or more classes.
 Next, create an object of each class
 Next, add all objects in a tuple.
 In the end, iterate the tuple using a for loop and call methods of a object
without checking its class.
Example
In the below example, fuel_type() and max_speed() are the instance methods created
in both classes.

class Ferrari:
def fuel_type(self):
print("Petrol")

def max_speed(self):
print("Max speed 350")

class BMW:
def fuel_type(self):
print("Diesel")

def max_speed(self):
print("Max speed is 240")

ferrari = Ferrari()
bmw = BMW()

# iterate objects of same type


for car in (ferrari, bmw):
# call methods without checking class of object
car.fuel_type()
car.max_speed()

Output

Petrol
Max speed 350

Diesel
Max speed is 240

As you can see, we have created two classes Ferrari and BMW. They have the same
instance method names fuel_type() and max_speed(). However, we have not linked
both the classes nor have we used inheritance.
We packed two different objects into a tuple and iterate through it using a car variable.
It is possible due to polymorphism because we have added the same method in both
classes Python first checks the object’s class type and executes the method present in
its class.
Polymorphism with Function and Objects
We can create polymorphism with a function that can take any object as a parameter
and execute its method without checking its class type. Using this, we can call object
actions using the same function instead of repeating method calls.
Example

class Ferrari:
def fuel_type(self):
print("Petrol")

def max_speed(self):
print("Max speed 350")

class BMW:
def fuel_type(self):
print("Diesel")

def max_speed(self):
print("Max speed is 240")

# normal function
def car_details(obj):
obj.fuel_type()
obj.max_speed()

ferrari = Ferrari()
bmw = BMW()

car_details(ferrari)
car_details(bmw)

Output

Petrol
Max speed 350
Diesel
Max speed is 240

Polymorphism In Built-in Methods


The word polymorphism is taken from the Greek words poly (many) and morphism
(forms). It means a method can process objects differently depending on the class
type or data type.
The built-in function reversed(obj) returns the iterable by reversing the given object.
For example, if you pass a string to it, it will reverse it. But if you pass a list of strings
to it, it will return the iterable by reversing the order of elements (it will not reverse
the individual string).
Let us see how a built-in method process objects having different data types.
Example:

students = ['Emma', 'Jessa', 'Kelly']


school = 'ABC School'

print('Reverse string')
for i in reversed('PYnative'):
print(i, end=' ')

print('\nReverse list')
for i in reversed(['Emma', 'Jessa', 'Kelly']):
print(i, end=' ')

Output:
Reverse string
evitanYP

Reverse list
Kelly Jessa Emma

Method Overloading
The process of calling the same method with different parameters is known as method
overloading. Python does not support method overloading. Python considers only the
latest defined method even if you overload the method. Python will raise a TypeError
if you overload the method.
Example

def addition(a, b):


c=a+b
print(c)

def addition(a, b, c):


d=a+b+c
print(d)

# the below line shows an error


# addition(4, 5)

# This line will call the second product method


addition(3, 7, 5)

To overcome the above problem, we can use different ways to achieve the method
overloading. In Python, to overload the class method, we need to write the method’s
logic so that different code executes inside the function depending on the parameter
passes.
For example, the built-in function range() takes three parameters and produce different
result depending upon the number of parameters passed to it.
Example:

for i in range(5): print(i, end=', ')


print()
for i in range(5, 10): print(i, end=', ')
print()
for i in range(2, 12, 2): print(i, end=', ')

Output:

0, 1, 2, 3, 4,
5, 6, 7, 8, 9,
2, 4, 6, 8, 10,

Let’s assume we have an area() method to calculate the area of a square and rectangle.
The method will calculate the area depending upon the number of parameters passed
to it.
 If one parameter is passed, then the area of a square is calculated
 If two parameters are passed, then the area of a rectangle is calculated.
Example: User-defined polymorphic method

class Shape:
# function with two default parameters
def area(self, a, b=0):
if b > 0:
print('Area of Rectangle is:', a * b)
else:
print('Area of Square is:', a ** 2)
square = Shape()
square.area(5)

rectangle = Shape()
rectangle.area(5, 3)

Output:

Area of Square is: 25


Area of Rectangle is: 15

Operator Overloading in Python


Operator overloading means changing the default behavior of an operator depending
on the operands (values) that we use. In other words, we can use the same operator for
multiple purposes.
For example, the + operator will perform an arithmetic addition operation when used
with numbers. Likewise, it will perform concatenation when used with strings.
The operator + is used to carry out different operations for distinct data types. This is
one of the most simple occurrences of polymorphism in Python.
Example:

# add 2 numbers
print(100 + 200)

# concatenate two strings


print('Jess' + 'Roy')

# merger two list


print([10, 20, 30] + ['jessa', 'emma', 'kelly'])

Output:

300
JessRoy
[10, 20, 30, 'jessa', 'emma', 'kelly']

Overloading + operator for custom objects


Suppose we have two objects, and we want to add these two objects with a
binary + operator. However, it will throw an error if we perform addition because the
compiler doesn’t add two objects. See the following example for more details.
Example:

class Book:
def __init__(self, pages):
self.pages = pages

# creating two objects


b1 = Book(400)
b2 = Book(300)

# add two objects


print(b1 + b2)

Output

TypeError: unsupported operand type(s) for +: 'Book' and 'Book'

We can overload + operator to work with custom objects also. Python provides some
special or magic function that is automatically invoked when associated with that
particular operator.
For example, when we use the + operator, the magic method __add__() is
automatically invoked. Internally + operator is implemented by
using __add__() method. We have to override this method in our class if you want to
add two custom objects.
Example:
class Book:
def __init__(self, pages):
self.pages = pages

# Overloading + operator with magic method


def __add__(self, other):
return self.pages + other.pages

b1 = Book(400)
b2 = Book(300)
print("Total number of pages: ", b1 + b2)

Output

Total number of pages: 700

Overloading the * Operator


The * operator is used to perform the multiplication. Let’s see how to overload it to
calculate the salary of an employee for a specific period. Internally * operator is
implemented by using the __mul__() method.
Example:

class Employee:
def __init__(self, name, salary):
self.name = name
self.salary = salary

def __mul__(self, timesheet):


print('Worked for', timesheet.days, 'days')
# calculate salary
return self.salary * timesheet.days
class TimeSheet:
def __init__(self, name, days):
self.name = name
self.days = days

emp = Employee("Jessa", 800)


timesheet = TimeSheet("Jessa", 50)
print("salary is: ", emp * timesheet)

Output

Wroked for 50 days


salary is: 40000

Magic Methods
In Python, there are different magic methods available to perform overloading
operations. The below table shows the magic methods names to overload the
mathematical operator, assignment operator, and relational operators in Python.
Operator Name Symbol Magic method
Addition + __add__(self, other)
Subtraction - __sub__(self, other)
Multiplication * __mul__(self, other)
Division / __div__(self, other)
Floor Division // __floordiv__(self,other)
Modulus % __mod__(self, other)
Power ** __pow__(self, other)
Increment += __iadd__(self, other)
Decrement -= __isub__(self, other)
Operator Name Symbol Magic method
Product *= __imul__(self, other)
Division /+ __idiv__(self, other)
Modulus %= __imod__(self, other)
Power **= __ipow__(self, other)
Less than < __lt__(self, other)
Greater than > __gt__(self, other)
Less than or equal to <= __le__(self, other)
Greater than or equal to >= __ge__(self, other)
Equal to == __eq__(self, other)
Not equal != __ne__(self, other)

What is Encapsulation in Python?


Encapsulation in Python describes the concept of bundling data and methods within
a single unit. So, for example, when you create a class, it means you are
implementing encapsulation. A class is an example of encapsulation as it binds all the
data members (instance variables) and methods into a single unit.
Example:
In this example, we create an Employee class by defining employee attributes such as
name and salary as an instance variable and implementing behavior
using work() and show() instance methods.

class Employee:
# constructor
def __init__(self, name, salary, project):
# data members
self.name = name
self.salary = salary
self.project = project
# method
# to display employee's details
def show(self):
# accessing public data member
print("Name: ", self.name, 'Salary:', self.salary)

# method
def work(self):
print(self.name, 'is working on', self.project)

# creating object of a class


emp = Employee('Jessa', 8000, 'NLP')

# calling public method of the class


emp.show()
emp.work()

Output:

Name: Jessa Salary: 8000


Jessa is working on NLP

Using encapsulation, we can hide an object’s internal representation from the outside.
This is called information hiding.
Also, encapsulation allows us to restrict accessing variables and methods directly and
prevent accidental data modification by creating private data members and methods
within a class.
Encapsulation is a way to can restrict access to methods and variables from outside of
class. Whenever we are working with the class and dealing with sensitive data,
providing access to all variables used within the class is not a good choice.
For example, Suppose you have an attribute that is not visible from the outside of an
object and bundle it with methods that provide read or write access. In that case, you
can hide specific information and control access to the object’s internal state.
Encapsulation offers a way for us to access the required variable without providing the
program full-fledged access to all variables of a class. This mechanism is used to
protect the data of an object from other objects.
Access Modifiers in Python
Encapsulation can be achieved by declaring the data members and methods of a class
either as private or protected. But In Python, we don’t have direct access modifiers
like public, private, and protected. We can achieve this by using
single underscore and double underscores.
Access modifiers limit access to the variables and methods of a class. Python provides
three types of access modifiers private, public, and protected.
 Public Member: Accessible anywhere from otside oclass.
 Private Member: Accessible within the class
 Protected Member: Accessible within the class and its sub-classes
Public Member
Public data members are accessible within and outside of a class. All member
variables of the class are by default public.
Example:

class Employee:
# constructor
def __init__(self, name, salary):
# public data members
self.name = name
self.salary = salary

# public instance methods


def show(self):
# accessing public data member
print("Name: ", self.name, 'Salary:', self.salary)
# creating object of a class
emp = Employee('Jessa', 10000)

# accessing public data members


print("Name: ", emp.name, 'Salary:', emp.salary)

# calling public method of the class


emp.show()

Output

Name: Jessa Salary: 10000


Name: Jessa Salary: 10000

Private Member
We can protect variables in the class by marking them private. To define a private
variable add two underscores as a prefix at the start of a variable name.
Private members are accessible only within the class, and we can’t access them
directly from the class objects.
Example:

class Employee:
# constructor
def __init__(self, name, salary):
# public data member
self.name = name
# private member
self.__salary = salary

# creating object of a class


emp = Employee('Jessa', 10000)
# accessing private data members
print('Salary:', emp.__salary)

Output

AttributeError: 'Employee' object has no attribute '__salary'

In the above example, the salary is a private variable. As you know, we can’t access
the private variable from the outside of that class.
We can access private members from outside of a class using the following two
approaches
 Create public method to access private members
 Use name mangling
Let’s see each one by one
Public method to access private members
Example: Access Private member outside of a class using an instance method

class Employee:
# constructor
def __init__(self, name, salary):
# public data member
self.name = name
# private member
self.__salary = salary

# public instance methods


def show(self):
# private members are accessible from a class
print("Name: ", self.name, 'Salary:', self.__salary)

# creating object of a class


emp = Employee('Jessa', 10000)
# calling public method of the class
emp.show()

Output:

Name: Jessa Salary: 10000

Name Mangling to access private members


We can directly access private and protected variables from outside of a class through
name mangling. The name mangling is created on an identifier by adding two leading
underscores and one trailing underscore, like this _classname__dataMember,
where classname is the current class, and data member is the private variable name.
Example: Access private member

class Employee:
# constructor
def __init__(self, name, salary):
# public data member
self.name = name
# private member
self.__salary = salary

# creating object of a class


emp = Employee('Jessa', 10000)

print('Name:', emp.name)
# direct access to private member using name mangling
print('Salary:', emp._Employee__salary)

Output

Name: Jessa
Salary: 10000

Protected Member
Protected members are accessible within the class and also available to its sub-classes.
To define a protected member, prefix the member name with a single underscore _.
Protected data members are used when you implement inheritance and want to allow
data members access to only child classes.
Example: Proctecd member in inheritance.

# base class
class Company:
def __init__(self):
# Protected member
self._project = "NLP"

# child class
class Employee(Company):
def __init__(self, name):
self.name = name
Company.__init__(self)

def show(self):
print("Employee name :", self.name)
# Accessing protected member in child class
print("Working on project :", self._project)

c = Employee("Jessa")
c.show()

# Direct access protected data member


print('Project:', c._project)
Output

Employee name : Jessa


Working on project : NLP
Project: NLP

Getters and Setters in Python


To implement proper encapsulation in Python, we need to use setters and getters. The
primary purpose of using getters and setters in object-oriented programs is to ensure
data encapsulation. Use the getter method to access data members and the setter
methods to modify the data members.
In Python, private variables are not hidden fields like in other programming
languages. The getters and setters methods are often used when:
 When we want to avoid direct access to private variables
 To add validation logic for setting a value
Example

class Student:
def __init__(self, name, age):
# private member
self.name = name
self.__age = age

# getter method
def get_age(self):
return self.__age

# setter method
def set_age(self, age):
self.__age = age

stud = Student('Jessa', 14)


# retrieving age using getter
print('Name:', stud.name, stud.get_age())

# changing age using setter


stud.set_age(16)

# retrieving age using getter


print('Name:', stud.name, stud.get_age())

Output

Name: Jessa 14
Name: Jessa 16

Let’s take another example that shows how to use encapsulation to implement
information hiding and apply additional validation before changing the values of your
object attributes (data member).
Example: Information Hiding and conditional logic for setting an object attributes

class Student:
def __init__(self, name, roll_no, age):
# private member
self.name = name
# private members to restrict access
# avoid direct data modification
self.__roll_no = roll_no
self.__age = age

def show(self):
print('Student Details:', self.name, self.__roll_no)
# getter methods
def get_roll_no(self):
return self.__roll_no

# setter method to modify data member


# condition to allow data modification with rules
def set_roll_no(self, number):
if number > 50:
print('Invalid roll no. Please set correct roll number')
else:
self.__roll_no = number

jessa = Student('Jessa', 10, 15)

# before Modify
jessa.show()
# changing roll number using setter
jessa.set_roll_no(120)

jessa.set_roll_no(25)
jessa.show()

Output:

Student Details: Jessa 10


Invalid roll no. Please set correct roll number

Student Details: Jessa 25

Advantages of Encapsulation
 Security: The main advantage of using encapsulation is the security of
the data. Encapsulation protects an object from unauthorized access. It
allows private and protected access levels to prevent accidental data
modification.
 Data Hiding: The user would not be knowing what is going on behind
the scene. They would only be knowing that to modify a data member,
call the setter method. To read a data member, call the getter method.
What these setter and getter methods are doing is hidden from them.
 Simplicity: It simplifies the maintenance of the application by keeping
classes separated and preventing them from tightly coupling with each
other.
 Aesthetics: Bundling data and methods within a class makes code
more readable and maintainable
What is Class Method in Python?
The @classmethod decorator is a built-in function decorator that is an expression
that gets evaluated after your function is defined. The result of that evaluation
shadows your function definition. A class method receives the class as an
implicit first argument, just like an instance method receives the instance
Syntax Python Class Method:
class C(object):
@classmethod
def fun(cls, arg1, arg2, ...):
....
fun: function that needs to be converted into a class method
returns: a class method for function.
 A class method is a method that is bound to the class and not the object of
the class.
 They have the access to the state of the class as it takes a class parameter
that points to the class and not the object instance.
 It can modify a class state that would apply across all the instances of the
class. For example, it can modify a class variable that will be applicable
to all the instances.
What is the Static Method in Python?
A static method does not receive an implicit first argument. A static method is
also a method that is bound to the class and not the object of the class. This
method can’t access or modify the class state. It is present in a class because it
makes sense for the method to be present in class.
Syntax Python Static Method:
class C(object):
@staticmethod
def fun(arg1, arg2, ...):
...
returns: a static method for function fun.
Class method vs Static Method
The difference between the Class method and the static method is:
 A class method takes cls as the first parameter while a static method needs
no specific parameters.
 A class method can access or modify the class state while a static method
can’t access or modify it.
 In general, static methods know nothing about the class state. They are
utility-type methods that take some parameters and work upon those
parameters. On the other hand class methods must have class as a
parameter.
 We use @classmethod decorator in python to create a class method and
we use @staticmethod decorator to create a static method in python.
When to use the class or static method?
 We generally use the class method to create factory methods. Factory
methods return class objects ( similar to a constructor ) for different use
cases.
 We generally use static methods to create utility functions.
How to define a class method and a static method?
To define a class method in python, we use @classmethod decorator, and to
define a static method we use @staticmethod decorator.
Let us look at an example to understand the difference between both of them. Let
us say we want to create a class Person. Now, python doesn’t support method
overloading like C++ or Java so we use class methods to create factory methods.
In the below example we use a class method to create a person object from birth
year.
As explained above we use static methods to create utility functions. In the below
example we use a static method to check if a person is an adult or not.
Below is the complete Implementation

# Python program to demonstrate


# use of class method and static method.
from datetime import date

class Person:
def __init__(self, name, age):
self.name = name
self.age = age

# a class method to create a Person object by birth year.


@classmethod
def fromBirthYear(cls, name, year):
return cls(name, date.today().year - year)

# a static method to check if a Person is adult or not.


@staticmethod
def isAdult(age):
return age > 18

person1 = Person('mayank', 21)


person2 = Person.fromBirthYear('mayank', 1996)

print(person1.age)
print(person2.age)

# print the result


print(Person.isAdult(22))

Output:
21
25
True

You might also like