OOPs in Python
OOPs in Python
OOPs in Python
1
Session Overview
OOPS in Python
Exercise
2
OOPS in Python
❖ It allows us to develop applications using an Object Oriented approach using classes and objects.
❑ Data Abstraction
❑ Encapsulation
❑ Polymorphism
❑ Inheritance
❑ Data Hiding
3
class Person: # Create objects for class Person
print(".................... Creating first object .......................")
# Attributes or variables p1 = Person() # p1 is the object of class Person. It is created by self
# Class attributes or class variables calling __init__() constructor
name = "Guest" # class attributes are shared by all objects p1.outputdetails()
age = 0 print("....................Input details for object 1 .....................")
salary = 0 p1.inputdetails("Aditi", 18)
p1.outputdetails()
# Create methods in this class
# constructor --> helps to instantiate an object. It is not compulsory. print(".................... Creating second object .......................")
def __init__(self): # self refers to the current or present object p2 = Person() # 2nd object. It is created by self calling __init__()
print("Instantiating the object............") constructor
self.salary = 1000 p2.outputdetails()
self.age = 18 print("..............Enter values for second object.......................")
p2.inputdetails()
# Other normal methods p2.outputdetails()
def inputdetails(self, n, a, s=5000): print(".................... Creating third object .......................")
self.name = n p3 = Person() # 3rd object. It is created by self calling __init__()
self.salary = s constructor
self.age = a p3.outputdetails()
def outputdetails(self):
print("The name is: ", self.name)
print("The age is: ", self.age)
print("The salary is: ", self.salary)
Class and Objects
What is a class and it’s object?
❖A Class is a user-defined prototype for an object that defines a set of attributes that
characterize any object of the class. It encapsulates or binds together the data members
(class variables and instance variables) and methods that use these data members
together in one unit.
❖A class contains data members and methods that use that data members.
❖An Object is the instance of a class and the process of creation of an object i.e. instance
of a class is called Instantiation. Example: An object obj1 that belongs to a class say,
Rectangle, for example, is an instance of the class Rectangle.
❖The method __init__() is a special method, which is called class constructor or
initialization method that Python calls when you create a new instance of this class.
❖You can use classes to model real-world objects, such as people, animals, vehicles,
books, and buildings. You can also use classes to model virtual objects, such as web
servers, directory trees, chatbots, and file managers
13
Defining classes in Python
How to create a class?
❖ The class statement creates a new class definition. The name of the class immediately follows the
keyword class followed by a colon as follows −
❖ Syntax of class :
class ClassName:
'Optional class documentation string’
class_suite
Where:
❑ The class has a documentation string, which can be accessed via ClassName.__doc__.
❑ The class_suite consists of all the component statements defining class members, data attributes
and functions.
14
Members of a Class
Who are the members of a class?
Members of a Python class are the attributes and methods that define the behavior and
properties of the objects created from the class:
1. Attributes or Variables: Variables defined inside class that store data for the class to work.
Types of attributes in a class:
a. Class attribute (Class variable)
b. Data or Instance Attribute (Instance variable)
2. Methods: Methods are the functions define the behavior of the class and its objects. They
are functions defined within the class and operate on class or instance data. A function that
is defined in a class definition and is using data members of the class for performing a
specified task. A method is called with an object of class using dot operator.
Types of methods in a class:
a. Instance methods
b. Class methods
c. Static methods
d. Magic (Dunder) Methods 15
Attributes - Class Attributes
Key features of Class attributes
16
Attributes - Data or Instance Attributes
Key features of Data or Instance attributes
17
class Vehicle: # Accessing instance attributes
wheels = 4 # Class attribute print(car.color) # Output: Red
def __init__(self, color): print(bike.color) # Output: Blue
self.color = color # Instance (data) attribute
# Changing the instance attribute
# Accessing class attribute car.color = "Green"
print(Vehicle.wheels) # Output: 4 print(car.color) # Output: Green
print(bike.color) # Output: Blue
# Creating instances with different data attributes
car = Vehicle("Red")
bike = Vehicle("Blue")
Operates on instance-specific
Instance Method None Yes No
attributes.
Operates on class-level
Class Method @classmethod No Yes
attributes or methods.
29
Session Overview
OOPS in Python
Exercise
30
Object Instantiation using Constructors
What is a constructor?
❑Parameterized Constructor
❑Non-parameterized Constructor
31
Creating the constructor
How to create a constructor?
❖ In python, the method __init__() calls the constructor of the class. This method is called when the class is instantiated to
❖ "__init__" is a reserved method in python classes. It is known as a constructor in object oriented concepts. This method
called when an object is created from the class and it allow the class to initialize the attributes of a class.
❖ Here self represents the instance of the class. By using the "self" keyword we can access the attributes and methods of the
class in python.
❖ To create object instances of a class, we call the class using class name and pass in whatever arguments its __init__() method accepts.
32
Class & Object Example
Class & Object Demonstration
class Student:
'Common base class for all Students’ # A documentation string, which can be accessed via ClassName.__doc__
stuCount = 0 # class variable declared
def displayCount(self):
print ("Total Students are %d" % Student.stuCount)
def displayStudent(self):
print ("Name : ", self.name, ", Age: ", self.age)
stu1 = Student(“Priya", 18) #This would create first object of Student class"
stu2 = Student(“Smriti", 20) #This would create second object of Student class"
stu1.displayStudent() # Calling method with object stu1
stu2.displayStudent () # Calling method with object stu2
print ("Total Students are %d" % Student.stuCount) # Accessing class variable stuCount shared by all objects
33
Built-In Class Attributes
Built-In Class Attributes
❖ Every Python class keeps following built-in attributes and they can be accessed using dot operator like any
other attribute −
❑ __module__ − Module name in which the class is defined. This attribute is "__main__" in
interactive mode.
❑ __bases__ − A possibly empty tuple containing the base classes, in the order of their occurrence in
the base class list.
34
Garbage Collection in Python
Freeing up the memory
❖Python deletes unneeded objects (built-in types or class instances) automatically to free the
memory space.
❖The process by which Python periodically reclaims blocks of memory that no longer are in use
is termed as Garbage Collection.
❖Python's garbage collector runs during program execution and is triggered when an object's
reference count reaches zero i.e. no reference to an object exists.
❖ User usually does not notice when the garbage collector destroys an object and reclaims its
space.
❖However, a class can implement the special method __del__(), called a destructor, that is
invoked when the instance is about to be destroyed. This method might be used to clean up
any non-memory resources used by an instance.
35
Use Destructors
How to use Destructors?
class Person:
def __init__(self, name):
self.name = name
print(f"Person {self.name} created.")
def __del__(self):
print(f"Person {self.name} deleted.")
# Create objects
p1 = Person("Alice")
p2 = Person("Bob")
print("End of program.") 36
Data Abstraction, Access
Modifiers, Encapsulation and
Inheritance
Data Abstraction
❖For example, a method that adds two integers. The internal processing
Can be accessed and modified Can be accessed and modified Cannot be accessed directly;
Access Outside Class
directly. (not recommended). requires getter/setter methods.
45
class BankAccount: # Usage
def __init__(self, account_holder, balance): account = BankAccount("Alice", 1000)
self.account_holder = account_holder # Public attribute
self.__balance = balance # Private attribute
# Access public attribute
# Getter method for private attribute print(account.account_holder) # Output: Alice
def get_balance(self):
return self.__balance # Access private attribute (not allowed)
# print(account.__balance) # Raises AttributeError
# Setter method with validation
def deposit(self, amount): # Access private data using methods
if amount > 0: print(account.get_balance()) # Output: 1000
self.__balance += amount
else: # Deposit and withdraw money
print("Deposit amount must be positive.") account.deposit(500)
print(account.get_balance()) # Output: 1500
# Method to withdraw money with validation
def withdraw(self, amount): account.withdraw(2000) # Output: Insufficient funds.
if amount > self.__balance: print(account.get_balance()) # Output: 1500
print("Insufficient funds.")
elif amount > 0:
self.__balance -= amount
else:
print("Withdrawal amount must be positive.")
Output:
Polymorphism
❖Polymorphism contains two words "poly" and "morphs". Poly means ‘many’ and
❑Overloading Methods
❑Overloading Operators
52
Method Overloading
Polymorphism through method calling
❖Overloading, in the context of programming, refers to the ability of a function or an operator to behave in
different ways depending on the parameters that are passed to the function, or the operands that the operator a
cts on.
❖Several ways to call a method is called polymorphism through method overloading. In Python you can define a
method in such a way that there are multiple ways to call a method.
❖Given a single method or function, we can specify the number of parameters during method calling.
❖Depending on the function definition, it can be called with zero, one, two or more parameters. This is known a
s method overloading.
❖Overloading a method fosters reusability. For instance, instead of writing multiple methods that differ only
slightly, we can write one method and overload it.
56
Method Overloading (contd.)
Polymorphism through method calling
To overload a user-defined function in Python, we need to write the function logic in such a way that depending
upon the parameters passed, a different piece of code executes inside the function. Take a look at the following
example:
class Employee:
def greeting(self, name=None):
if name is not None:
print('Hello Employee: ' + name)
else:
print('Hello Guest')
❖When we use an operator on user defined data types then automatically a special function or
magic function associated with that operator is invoked. Changing the behavior of operator is as
simple as changing the behavior of method or function.
❖To perform operator overloading, Python provides some special function or Magic Function
that is automatically invoked when it is associated with that particular operator. For
example, when we use + operator, the magic method __add__ is automatically invoked in
which the operation for + operator is defined.
❖By changing this magic method’s code, we can give extra meaning to the + operator.
❖Suppose you have created a class to represent vectors. Suppose we want to use the simple
mathematical addition (+) operator to add them. Python does not allow this!!.
❖For this we must define an __add__ method in our class to perform vector addition and then the
addition operator (+) will add the two vectors in the same way as it adds two numbers: 58
Magic Method for operator overloading Operator/Action Example
__le__(self, other) <= (Less than or equal to) obj1 <= obj2
__ge__(self, other) >= (Greater than or equal to) obj1 >= obj2
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Vector({self.x}, {self.y})"
# Creating objects
v1 = Vector(2, 3)
v2 = Vector(4, 1)
❖ Inheritance is the object-oriented programming concept where an object is based on another object.
❖ The object that is getting inherited is called superclass and the object that inherits the superclass is called
subclass.
❖ Instead of starting from a scratch, you can create a class by deriving it from a pre-existing class by listing
❖ The child class inherits the attributes of its parent class, and you can use those attributes as if they were
defined in the child class. A child class can also override data members and methods from the parent.
64
Inheritance (contd.)
Syntax & Practical Demo
❖ Syntax:
❖ Derived classes are declared much like their parent class; however, a list of base classes to
inherit from is given after the class name −
65
Inheritance (contd.)
Syntax & Practical Demo
# Child Class
class Car(Vehicle):
def __init__(self, brand, model, doors):
super().__init__(brand, model) # Call the parent class's constructor
self.doors = doors
def details(self):
return f"Car: {self.brand} {self.model}, Doors: {self.doors}"
66
Types of Inheritance
Single Inheritance
68
class ParentClass:
def __init__(self):
self.value = 10
def display(self):
print("ParentClass display:", self.value)
class ChildClass(ParentClass):
def __init__(self):
super().__init__()
self.child_value = 20
def display_child(self):
print("ChildClass display:", self.child_value)
70
class ParentClass1: # Creating an instance of ChildClass
def __init__(self): child = ChildClass()
self.value1 = 10 child.display1() # Inherited from ParentClass1
child.display2() # Inherited from ParentClass2
def display1(self): child.display_child() # Defined in ChildClass
print("ParentClass1 display:", self.value1)
class ParentClass2:
def __init__(self):
self.value2 = 20
def display2(self):
print("ParentClass2 display:", self.value2)
def display_child(self):
print("ChildClass display: Combined values:", self.value1, self.value2)
Types of Inheritance
Multi-Level Inheritance
72
class ParentClass: # Creating an instance of GrandChildClass
def __init__(self): grandchild = GrandChildClass()
self.value = 10 grandchild.display() # Inherited from ParentClass
grandchild.display_child() # Inherited from ChildClass
def display(self): grandchild.display_grandchild() # Defined in GrandChildClass
print("ParentClass display:", self.value)
class ChildClass(ParentClass):
def __init__(self):
super().__init__()
self.child_value = 20
def display_child(self):
print("ChildClass display:", self.child_value)
class GrandChildClass(ChildClass):
def __init__(self):
super().__init__()
self.grandchild_value = 30
def display_grandchild(self):
print("GrandChildClass display:", self.grandchild_value)
Types of Inheritance
Hierarchical Inheritance
74
class ParentClass:
def __init__(self):
self.value = 10
def display(self):
print("ParentClass display:", self.value)
class ChildClass1(ParentClass):
def display_child1(self):
print("ChildClass1 display")
class ChildClass2(ParentClass):
def display_child2(self):
print("ChildClass2 display")
76
class ParentClass1: class GrandChildClass(ParentClass2, ChildClass):
def __init__(self): def __init__(self):
self.value1 = 10 ParentClass2.__init__(self)
ChildClass.__init__(self)
def display1(self):
print("ParentClass1 display:", self.value1) def display_grandchild(self):
print("GrandChildClass display:", self.value2,
class ParentClass2: self.child_value)
def __init__(self):
self.value2 = 20 # Creating an instance of GrandChildClass
grandchild = GrandChildClass()
def display2(self): grandchild.display1() # Inherited from ParentClass1
print("ParentClass2 display:", self.value2) grandchild.display2() # Inherited from ParentClass2
grandchild.display_child() # Inherited from ChildClass
class ChildClass(ParentClass1): grandchild.display_grandchild() # Defined in GrandChildClass
def __init__(self):
super().__init__()
self.child_value = 30
def display_child(self):
print("ChildClass display:", self.child_value)
Inheritance Type Description Example
➢ MRO is a concept used in inheritance. It is the order in which a method is searched for in a
classes hierarchy and is especially useful in Python because Python supports multiple
inheritance.
➢ In Python, the MRO is from bottom to top and left to right. This means that, first, the method
is searched in the class of the object. If it’s not found, it is searched in the immediate super
class.
➢ In the case of multiple super classes, it is searched left to right, in the order by which was
declared by the developer. For
➢ Example:
def class C(B, A)
➢ In this case, the MRO would be C -> B -> A. Since B was mentioned first in the class
declaration, it will be searched for first while resolving a method. 79
Method Resolution Order (MRO)
Resolve Method calls and decide order of Parent Classes
➢ Example:
def class C(B, A)
➢ In this case, the MRO would be C -> B -> A. Since B was mentioned first in the class
declaration, it will be searched for first while resolving a method.
Eg: C.mro()
Eg: C.__mro__
80
class A: # Creating an instance of D
pass obj = D()
The super() function automatically resolves the MRO and ensures that the correct parent method is called.
This is particularly useful in complex hierarchies with multiple inheritance. 86
Example 1: Calling Parent Constructor
Example 1: Calling Parent Constructor
class Parent:
def __init__(self, name):
print("In Parent Constructor!!")
self.name = name
class Child(Parent):
def __init__(self, name, age):
super().__init__(name) # Call Parent class's constructor
print("In Child Constructor!!")
self.age = age
def display(self):
print(f"Name: {self.name}, Age: {self.age}")
class Parent:
def greet(self):
print("Hello from Parent!")
class Child(Parent):
def greet(self):
super().greet() # Call Parent class's greet method
print("Hello from Child!")
88