[go: up one dir, main page]

0% found this document useful (0 votes)
6 views23 pages

PLC142_(Python)_Unit-4(OOPs) A3

The document provides an overview of Object-Oriented Programming (OOP) in Python, explaining its key concepts such as classes, objects, attributes, and methods. It contrasts OOP with Procedural-Oriented Programming (POP) and highlights the advantages of OOP, including better organization, code reuse, and data protection. Additionally, it covers the creation of classes and objects, the role of the __init__() constructor, and the significance of the self parameter.

Uploaded by

driti s gowda
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)
6 views23 pages

PLC142_(Python)_Unit-4(OOPs) A3

The document provides an overview of Object-Oriented Programming (OOP) in Python, explaining its key concepts such as classes, objects, attributes, and methods. It contrasts OOP with Procedural-Oriented Programming (POP) and highlights the advantages of OOP, including better organization, code reuse, and data protection. Additionally, it covers the creation of classes and objects, the role of the __init__() constructor, and the significance of the self parameter.

Uploaded by

driti s gowda
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/ 23

PLC142 (Python): Unit-4(OOPs)

📌 1️⃣ Introduction to Object-Oriented Programming (OOP)


What is Object-Oriented Programming (OOP)?
Object-Oriented Programming (OOP) is a programming approach that organizes code into objects rather than just
functions and logic.
Each object represents something in the real world and contains:
✅ Data (attributes) → Information about the object
✅ Behavior (methods) → Actions the object can perform
Difference Between Procedural Programming and OOP

OOP (Object-Oriented Programming) is based on the concept of objects and classes. It helps in organizing code
better by bundling data and functions together.

POP (Procedural-Oriented Programming) follows a step-by-step, top-down approach, where code is written using
functions and procedures without focusing on objects.

OOP provides concepts like inheritance, polymorphism, and encapsulation, making it easier to manage large
projects.

POP is simpler and better suited for small programs where defining multiple functions is enough.

OOP makes code more reusable and scalable, while POP can become messy and harder to maintain as the project
grows.

Example: Procedural vs OOP

🚀 Procedural Approach (Without OOP)


name = "Alice"
age = 20

def display(name, age):


print(f"Name: {name}, Age: {age}")

display(name, age)

📌 Problem: With multiple students, we need multiple variables and functions.


🚀 OOP Approach (With Classes & Objects)
class Student:
def __init__(self, name, age):
self.name = name
self.age = age

def display(self):
print(f"Name: {self.name}, Age: {self.age}")

# Creating objects
s1 = Student("Alice", 20)
s1.display()

📌 Why is this better?


PLC142 (Python): Unit-4(OOPs) 1
Each student is an object with its own data and behavior.

We can easily create multiple students without writing extra code.

Data is inside the object, making it more organized.

Why Use OOP?


OOP is powerful because it helps us:

✅ Organize code better → Everything related to an object stays within it.


✅ Reuse code easily → Inheritance lets us extend existing classes.
✅ Make programs easier to understand → Code mirrors real-world objects.
✅ Protect data → Encapsulation helps in hiding sensitive data.
🔹 Real-World Example of OOP
Think of a Car 🚗:
The Car class has attributes (color, brand, speed).

It has methods (brake, horn).

A sports car can be a child class of Car (inherits from Car but can add its own methods (like turbo_mode ) and
attributes (spoiler)).

This is how OOP works in programming!

🌟 Summary
✅ OOP organizes programs into objects instead of just functions.
✅ It helps reuse code and makes programs easier to manage.
✅ Four key concepts: Encapsulation, Inheritance, Polymorphism, Abstraction.
✅ Example: A Car class can have attributes (brand, speed) and methods (drive, honk).
📌 2️⃣ Classes and Objects in Python
1️⃣ What is a Class?
A class is a blueprint or template used to create objects.

It defines:

Attributes (Variables) → Data that belongs to the object.

Methods (Functions) → Actions the object can perform.

🔹 Example:
Think of a Car blueprint. It describes the car’s color, speed, brand, and behaviors like driving or braking. But this
blueprint itself isn’t a car—it’s just a design.

2️⃣ What is an Object?


An object is an actual instance of a class.

Every object has its own:



Unique data (attributes)
✅ Defined behaviors (methods)
🔹 Example:
A real car 🚗 with ,
color = "red" speed = 200 , brand = "BMW" is an object of the Car class.

3️⃣ Creating a Class in Python


PLC142 (Python): Unit-4(OOPs) 2
Syntax : ( class definition)

class classname:
//body of class

We define a class using the class keyword:

Example :

class Car:
pass # Empty class (Just a blueprint)

📌 pass is used when we don’t want to define any methods or attributes yet.

4️⃣ Creating Objects (Instances)


syntax : (this process is also called as instantiation)

oobject_name = classname(attributes)

Example :

Once a class is defined, we can create objects (instances) from it.

class Car:
def __init__(self, brand, color, speed): # Constructor
self.brand = brand
self.color = color
self.speed = speed

# Creating objects
car1 = Car("BMW", "Red", 200)
car2 = Car("Audi", "Blue", 180)

print(car1.brand) # Output: BMW


print(car2.color) # Output: Blue

5️⃣ Understanding the __init__() Method (Constructor)


The __init__() method is a special function that runs automatically whenever an object is created.
It is used to initialize attributes of an object.

Every class should have a method with the special name init.

This initialize method is automatically called whenever a new instance of


Point is created.

It gives the programmer the opportunity to set up the attributes required


within the new instance by giving them their initial state/values.

🔹 Example:
class Car:
def __init__(self, brand, color, speed):
self.brand = brand
self.color = color
self.speed = speed

✅ Whenever we create an object, __init__() runs automatically.

PLC142 (Python): Unit-4(OOPs) 3


✅ It assigns values to the object’s attributes ( self.brand , self.color , etc.).

6️⃣ What is self in Python?

🔍 Definition
self is a reference to the current object.

It allows each object to access its own attributes and methods.

🔹 Why Do We Need self ?


Without self , Python would not know which object's data to modify.

🔹 Example Without self (Incorrect)

class Car:
def __init__(brand, color, speed): # ❌ Missing self
brand = brand # ❌ Won't work
color = color # ❌ Won't work
speed = speed # ❌ Won't work

car1 = Car("BMW", "Red", 200)


print(car1.brand) # ❌
ERROR: AttributeError

🚨 Problem:
Python does not know that brand belongs to car1 because there is no self .

The assignment brand = brand does nothing!

✅ Correct Version Using self

class Car:
def __init__(self, brand, color, speed): # ✅ `self` is added
self.brand = brand
self.color = color
self.speed = speed

car1 = Car("BMW", "Red", 200)


print(car1.brand) # ✅
Output: BMW

🔹 How Does self Work?


When we create an object like car1 = Car("BMW", "Red", 200) ,

Python translates it to:

Car.__init__(car1, "BMW", "Red", 200)

Here, car1 is passed as self . That’s why self.brand = brand assigns "BMW" to car1.brand .

7️⃣ Methods in a Class


Methods are functions inside a class that allow objects to perform actions.

class Car:
def __init__(self, brand, color, speed):
self.brand = brand
self.color = color
self.speed = speed

def display_info(self): # ✅ Method inside class


PLC142 (Python): Unit-4(OOPs) 4
print(f"Brand: {self.brand}, Color: {self.color}, Speed: {self.speed}")

car1 = Car("BMW", "Red", 200)


car1.display_info() # Output: Brand: BMW, Color: Red, Speed: 200

📌 Why use methods?


They allow objects to perform actions.

They can access and modify object attributes.

8️⃣ Creating Multiple Objects


We can create as many objects as needed from a class.

car1 = Car("Tesla", "White", 250)


car2 = Car("Ford", "Black", 220)
car3 = Car("Mercedes", "Silver", 230)

car1.display_info()
car2.display_info()
car3.display_info()

📌 Each object stores unique data but uses the same class definition.
9️⃣ What Happens in Memory?
When we create an object, Python:

1. Allocates memory for the object.

2. Runs __init__() to initialize values.

3. Stores the object’s address in a variable.

🔍 Object Memory Reference Example


car1 = Car("BMW", "Red", 200)
car2 = car1 # Both point to the same object!

print(car1.brand) # Output: BMW


print(car2.brand) # Output: BMW

car2.brand = "Audi"
print(car1.brand) # Output: Audi (Changes in car2 affect car1)

📌 Why?
car2 = car1 does not create a new object.

It makes car2 a reference to car1 .

To create an independent copy, use .copy() or copy.deepcopy() .

🔟 Summary
✅ A class is a blueprint for objects.
✅ An object is an instance of a class.
✅ is a constructor that runs automatically.
__init__()

✅ refers to the current object and is required in every method.


self

✅ Methods allow objects to perform actions.

PLC142 (Python): Unit-4(OOPs) 5


✅ Multiple objects share the same class definition but have unique data.
📌 3️⃣ Attributes and Methods in Python OOP
1️⃣ What Are Attributes?
Attributes are variables inside a class that store information about an object.
Each object has its own attributes, which define its properties.

🔹 Key Characteristics of Attributes


✔ Defined inside the class but accessed through objects.
✔ Each object gets its own copy of attributes.
✔ Can be modified and updated for each object.
🔹 Example of Attributes
class Student:
def __init__(self, name, age):
self.name = name # Attribute
self.age = age # Attribute

# Creating objects
s1 = Student("Alice", 20)
s2 = Student("Bob", 22)

print(s1.name) # Output: Alice


print(s2.age) # Output: 22

📌 Explanation
✅ and are attributes that store student details.
name age

✅ and have different attribute values.


s1 s2

✅ Attributes belong to an object, so each object can store unique data.


2️⃣ What Are Methods?
Methods are functions defined inside a class that define an object's behavior.

🔹 Key Characteristics of Methods


✔ Operate on object attributes.
✔ Belong to a specific object and can modify its attributes.
✔ Always take as the first parameter to refer to the object itself.
self

🔹 Example of Methods
class Student:
def __init__(self, name, age):
self.name = name
self.age = age

def display(self): # ✅
Method
print(f"Student: {self.name}, Age: {self.age}")

# Creating object

PLC142 (Python): Unit-4(OOPs) 6


s1 = Student("Alice", 20)
s1.display() # Output: Student: Alice, Age: 20

📌 Explanation
✅ is a method because it is inside a class.
display()

✅ It prints student details using andself.name. self.age

✅ It is called using an object, not directly like a function.


3️⃣ Difference Between Methods and Functions
Methods and functions serve different purposes in Python, with key differences in how they work and what they can do.

Feature Methods Functions

Defined Inside A class Outside a class

Called Using object.method_name() function_name()

Uses self ? ✅ Yes (Refers to the object) ❌ No


Belongs to A specific object Independent (not tied to an object)

Can Modify Attributes? ✅ Yes ❌ No


🔹 Example of a Function (Outside a Class)
def greet(name): # Function ✅
print(f"Hello, {name}!")

greet("Alice") # Output: Hello, Alice!

📌 Explanation
✅ is a regular function, not linked to any object.
greet()

✅ It does not use because it does not belong to a class.


self

✅ It can be called independently without an object.


🔹 Example of a Method (Inside a Class)
class Student:
def __init__(self, name):
self.name = name

def greet(self): # Method ✅


print(f"Hello, {self.name}!")

s1 = Student("Alice")
s1.greet() # Output: Hello, Alice!

📌 Explanation
✅ is a method because it is inside a class.
greet()

✅ It uses , meaning it belongs to an object.


self.name

✅ It cannot be called directly like a function; we must use s1.greet() .

4️⃣ Why Are Attributes and Methods Important?


✔ They store and manage object-specific data.
✔ They define how objects behave and interact.
PLC142 (Python): Unit-4(OOPs) 7
✔ They allow code reuse by encapsulating properties and behaviors in a single place.
🌟 Summary
✅ Attributes store object data, while methods define object behavior.
✅ Methods are inside a class, whereas functions exist outside a class.
✅ Methods use to access object data, but functions do not.
self

✅ Methods operate on objects, while functions work independently..


📌 4️⃣ Instances as Arguments and Return Values in Python
OOP
1️⃣ What is an Instance?
An instance is simply an object created from a class.

📌 Example:
class Car:
def __init__(self, brand):
self.brand = brand

car1 = Car("BMW") # ✅ car1 is an instance (object) of the Car class


car2 = Car("Audi") # ✅ car2 is also an instance

✅ Every object of a class is an instance.


2️⃣ Passing an Instance as an Argument
In Python, you can pass an object (instance) as an argument to a method or function.
This enables objects to interact with each other.

🔹 Example: Passing an Object to a Method


class Car:
def __init__(self, brand, speed):
self.brand = brand
self.speed = speed

def compare_speed(self, other_car): # ✅ Method that takes another object


if self.speed > other_car.speed:
print(f"{self.brand} is faster than {other_car.brand}")
else:
print(f"{other_car.brand} is faster than {self.brand}")

car1 = Car("BMW", 220)


car2 = Car("Audi", 200)

car1.compare_speed(car2) # Output: BMW is faster than Audi

📌 Explanation
✔ is a method that accepts another object (
compare_speed() ) as an argument.
other_car

✔ It compares the speed of two different car objects.


✔ refers to the current object, and
self is the object passed as an argument.
other_car

3️⃣
PLC142 (Python): Unit-4(OOPs) 8
3️⃣ Returning an Instance from a Method
Methods can return an object (instance) as their result.

This is particularly useful for creating new modified objects inside a method.

🔹 Example: Returning an Object from a Method


class Student:
def __init__(self, name, marks):
self.name = name
self.marks = marks

def add_bonus(self, extra_marks): # ✅ Method that returns a new object


return Student(self.name, self.marks + extra_marks)

s1 = Student("Alice", 85)
s2 = s1.add_bonus(5) # ✅ Creates a new object with updated marks
print(s1.marks) # Output: 85
print(s2.marks) # Output: 90

📌 Explanation
✔ returns a new instance with updated marks.
add_bonus()

✔ is a new object, leaving unchanged.


s2 s1

✔ This approach maintains immutability (preserving original data).


4️⃣ Summary
✅ Instances are objects created from a class.
✅ Objects can be passed as arguments to methods or functions.
✅ Methods can return new objects, enabling modifications while preserving originals.
✅ These concepts are fundamental to creating interactive and dynamic object-oriented programs.
📌5️⃣ Justifying the Statement: "Objects Are Mutable"
1️⃣ What Does "Objects Are Mutable" Mean?
In Python, an object is an instance of a class, and mutability means that we can modify the attributes of an object
after it has been created.
Unlike immutable types, objects allow changes to their internal state without needing to create a new instance.

2️⃣ Demonstrating Object Mutability


📌 Objects are mutable because we can modify their attributes after creation.
🔹 Example: Modifying Object Attributes
class Car:
def __init__(self, brand, speed):
self.brand = brand
self.speed = speed

car1 = Car("BMW", 200) # ✅ Creating an object


print("Before modification:", car1.speed)

PLC142 (Python): Unit-4(OOPs) 9


car1.speed = 220 # ✅ Modifying the object’s attribute
print("After modification:", car1.speed)

📌 Explanation:
✔ is an object of the class.
car1 Car

✔ We modified the attribute from to without creating a new object.


speed 200 220

✔ The same memory location is used, proving that objects are mutable.
3️⃣ Objects Can Be Modified Through Methods
📌 Instead of modifying attributes directly, we can use methods inside a class to change object data.
🔹 Example: Using a Method to Modify an Object
class Car:
def __init__(self, brand, speed):
self.brand = brand
self.speed = speed

def update_speed(self, new_speed): # ✅ Method to modify object


self.speed = new_speed

car1 = Car("BMW", 200)


print("Before modification:", car1.speed)

car1.update_speed(250) # ✅ Changing object’s attribute using a method


print("After modification:", car1.speed)

📌 Explanation:
✔ allows us to change an object's attribute dynamically.
update_speed()

✔ The object remains the same, proving objects are mutable.


4️⃣ Why Is Object Mutability Useful?
📌 Advantages of Mutable Objects:
✔ Allows dynamic modifications → Objects can store changing data (e.g., real-time updates in a game or application).
✔ Efficient memory usage → No need to create a new object each time an attribute changes.
✔ Helps in object-oriented programming → Objects evolve over time as their state updates.
5️⃣ Summary
✅ Objects are mutable because we can modify their attributes after creation.
✅ Methods inside a class can be used to update object data dynamically.
✅ Mutability is useful for efficient memory usage and dynamic behavior in object-oriented programming.
📌6️⃣ Inheritance in Python
1️⃣ What is Inheritance?
Inheritance allows a child class to reuse the attributes and methods of a parent class.
Instead of writing the same code again, the child class inherits behavior from the parent class while also having the
option to extend or modify it.

PLC142 (Python): Unit-4(OOPs) 10


🔹 Simple Real-Life Example
Think of a Car 🚗 and a SportsCar 🏎️:
A Car has basic features like brand and drive() .

A SportsCar is still a car but has extra features like turbo_mode() .

Instead of rewriting everything, SportsCar can inherit from Car and just add its extra features.

2️⃣ The __init__() Function in Inheritance


The __init__() method is a constructor that runs automatically when an object is created.

🔹 Basic Syntax:
class Parent:
def __init__(self, param1):
self.param1 = param1 # Parent class attribute

When a child class inherits from a parent class, it can:

1. Use the parent class's __init__() method as it is.

2. Extend the __init__() method by adding new attributes.

3. Modify the __init__() method completely.

3️⃣ Adding Attributes in the Child Class


A child class inherits attributes from the parent but can also add its own attributes.

🔹 Example: Car and SportsCar


class Car:
def __init__(self, brand):
self.brand = brand # Parent attribute

class SportsCar(Car): # Inherits from Car


def __init__(self, brand, turbo): # ✅
New attribute added
super().__init__(brand) # Calls parent constructor
self.turbo = turbo # New attribute for SportsCar

📌 Key Takeaways:
✔ inherits the
SportsCar attribute from
brand . Car

✔ It adds a new attribute , which is specific to sports cars.


turbo

✔ ensures that the parent class is properly initialized.


super().__init__(brand)

4️⃣ Adding Methods in the Child Class


The child class can also add new methods that are not in the parent class.

🔹 Example: Car and SportsCar (Adding Methods)


class Car:
def __init__(self, brand):
self.brand = brand

def drive(self): # Parent method


print(f"{self.brand} is driving.")

class SportsCar(Car):

PLC142 (Python): Unit-4(OOPs) 11


def __init__(self, brand, turbo):
super().__init__(brand)
self.turbo = turbo

def turbo_mode(self): # ✅
New method added in child class
print(f"{self.brand} is in turbo mode!")

📌 Key Takeaways:
✔ The SportsCar class inherits drive() from Car.
✔ It adds a new method turbo_mode() that is specific to sports cars.

5️⃣ Full Example of super()


The super() function is used to call methods from the parent class inside a child class.
It helps the child class reuse attributes and methods without rewriting code.

🔹 Full Example: Car and SportsCar Using super()

class Car:
def __init__(self, brand, speed):
self.brand = brand
self.speed = speed

def drive(self):
print(f"{self.brand} is driving at {self.speed} km/h.")

class SportsCar(Car):
def __init__(self, brand, speed, turbo):
super().__init__(brand, speed) # Calls parent constructor
self.turbo = turbo # New attribute specific to SportsCar

def drive(self): # ✅
Overriding the drive method
super().drive() # ✅
Calls the parent class method
print(f"{self.brand} is in turbo mode with speed {self.speed + 50} km/h!") # Modified behavior

# Creating an object of the child class


my_car = SportsCar("BMW", 200, True)

my_car.drive()

🔹 Expected Output
BMW is driving at 200 km/h.
BMW is in turbo mode with speed 250 km/h!

📌 Explanation:
✔ → Calls the parent’s constructor.
super().__init__(brand, speed)

✔ → Calls the parent’s


super().drive() method before modifying behavior.
drive()

✔ The child class inherits, extends, and modifies behavior.


6️⃣Method Overriding (Changing Parent Methods)
A child class can change the behavior of any method from its parent class.

PLC142 (Python): Unit-4(OOPs) 12


class Car:
def drive(self):
print("Car is driving.")

class SportsCar(Car):
def drive(self): # Overriding the parent method
print("SportsCar is driving at high speed!")

my_car = SportsCar()
my_car.drive() # Output: SportsCar is driving at high speed!

📌 Why Override?
✔ Allows the child class to modify the parent's behavior without changing the parent class.
7️⃣Summary
✅ Inheritance allows a class to reuse another class’s attributes and methods.
✅ in Inheritance helps the child class initialize attributes properly.
__init__()

✅ Child classes can add new attributes specific to their behavior.


✅ Child classes can add new methods that are not in the parent class.
✅ is used to call parent class methods and constructors inside the child class.
super()

📌 7️⃣ Polymorphism in Python OOP


1️⃣ What is Polymorphism?
Polymorphism means "many forms", allowing the same method or function to work with different types of objects.

🔹 Why is Polymorphism Important?


✔ Code Flexibility → Functions/methods work with different data types.
✔ Reusability → Avoids rewriting the same logic for different objects.
✔ Extensibility → New classes can be added without modifying existing code.
2️⃣Functions
Python Program to Demonstrate In-Built Polymorphic

Python has built-in functions that exhibit polymorphism by working on multiple data types.

🔹 Example: Using len() on Different Data Types

print(len("Hello")) #✅ Works with strings (Output: 5)


print(len([1, 2, 3, 4])) #✅ Works with lists (Output: 4)
print(len({"a": 1, "b": 2})) # ✅Works with dictionaries (Output: 2)

📌 Key Takeaways:
✔ The same function works with strings, lists, and dictionaries.
len()

✔ Python automatically determines how behaves based on the data type.


len()

3️⃣📌 Polymorphism with Class Methods


Different classes can have the same method name, and we can call them without knowing the specific class type.

🔹 Example: Using Polymorphism with Different Classes


PLC142 (Python): Unit-4(OOPs) 13
class Dog:
def sound(self):
print("Dogs bark.")

class Cat:
def sound(self):
print("Cats meow.")

class Cow:
def sound(self):
print("Cows moo.")

# Creating objects
dog = Dog()
cat = Cat()
cow = Cow()

# Calling the same method "sound" on different objects


dog.sound()
cat.sound()
cow.sound()

🔹 Expected Output
Dogs bark.
Cats meow.
Cows moo.

📌 Explanation of Polymorphism in This Code


1️⃣ Each class ( , , ) has a method named .
Dog Cat Cow sound()

2️⃣ Each method behaves differently depending on the class it belongs to.
sound()

3️⃣ We create separate objects ( , , ) and call individually.


dog cat cow sound()

4️⃣ Even though we call the same method name, the output is different for each object.
📌 Key Takeaways for Polymorphism with Class Methods
✅ Different classes have the same method name ( ). sound()

✅ Each class provides its own version of the method.


✅ No need for loops—we simply call the method on each object.
4️⃣📌 Polymorphism with Inheritance
Polymorphism allows child classes to override parent class methods while keeping the same method name.
📌 The parent class defines a general method, and the child classes modify it as needed.
🔹 Example: Animal and Different Sounds
class Bird:
def intro(self):
print("There are many types of birds.")

def flight(self):
print("Most of the birds can fly but some cannot.")

PLC142 (Python): Unit-4(OOPs) 14


class sparrow(Bird):
def flight(self):
print("Sparrows can fly.")

class ostrich(Bird):
def flight(self):
print("Ostriches cannot fly.")

# Creating objects
obj_bird = Bird()
obj_spr = sparrow()
obj_ost = ostrich()

# Calling methods
obj_bird.intro()
obj_bird.flight()

obj_spr.intro()
obj_spr.flight()

obj_ost.intro()
obj_ost.flight()

🔹 Expected Output
There are many types of birds.
Most of the birds can fly but some cannot.

There are many types of birds.


Sparrows can fly.

There are many types of birds.


Ostriches cannot fly.

📌Explanation of Polymorphism in This Code


1️⃣ The Bird class (Parent Class) defines two methods:

intro() → Prints a general message about birds.

flight() → Prints a general statement that most birds can fly.

2️⃣ The sparrow and ostrich classes (Child Classes) inherit from Bird but override the flight() method with different
behaviors:

sparrow.flight() prints "Sparrows can fly."

ostrich.flight() prints "Ostriches cannot fly."

3️⃣ Polymorphism occurs because:


All objects ( obj_bird , obj_spr , obj_ost ) use the same method name flight() .

The behavior of flight() changes based on the class instance used.

📌 Key Takeaways:
✅Same method name ( ) is used in multiple classes.
flight()

✅Child classes override parent class methods with their own behavior.
✅Different objects exhibit different behaviors for the same method.

PLC142 (Python): Unit-4(OOPs) 15


🌟✅ Summary
Polymorphism allows a method or function to work with multiple object types.
✅ Built-in functions like exhibit polymorphism.
len()

✅ Polymorphism with class methods → The same method name in different classes.
✅ Polymorphism with inheritance → Child classes override parent methods.
📌 8️⃣ Operator Overloading in Python
📌 What is Operator Overloading?
Operator overloading allows us to change the behavior of operators ( + , - , * , / , == , > , etc.) when used with user-
defined objects.
Python provides special methods (also called magic or dunder methods) that we can define inside a class to
customize how operators work with objects.
For example, instead of writing a method to add two objects, we can simply use + if we define the __add__() method.

📌 Why Use Operator Overloading?


Operator overloading helps in:
✅ Making code more readable and natural (e.g., using instead of ). + add()

✅ Allowing built-in operators to work with user-defined classes.


✅ Reducing complexity by replacing long method calls with simple operators.
✅ Making classes behave like built-in types (e.g., numbers, strings).
✅ Supporting mathematical and comparison operations on objects.
📌 Example: Overloading the + Operator

🔹 Without Operator Overloading


class Point:
def __init__(self, x, y):
self.x = x
self.y = y

p1 = Point(2, 3)
p2 = Point(4, 5)

# We cannot directly add two objects


p3 = p1 + p2 # ❌
ERROR: Unsupported operand type

📌 Problem: Python does not know how to add two Point objects.

🔹 With Operator Overloading


We define __add__() to tell Python how to add Point objects.

class Point:
def __init__(self, x, y):
self.x = x
self.y = y

def __add__(self, other): # Overloading +


return Point(self.x + other.x, self.y + other.y)

PLC142 (Python): Unit-4(OOPs) 16


# Creating two objects
p1 = Point(2, 3)
p2 = Point(4, 5)

# Now we can use +


p3 = p1 + p2

print(f"Result: ({p3.x}, {p3.y})") # Output: (6, 8)

📌 Why is this better?


✅ The operator now works with objects.
+ Point

✅ The code is cleaner and more intuitive.


✅ We don’t need a separate method. add()

📌 Types of Operator Overloading


Python allows us to overload different types of operators:

1️⃣ Binary Operators ( +, , , / , etc.)


These operators work with two operands (e.g., a+b ).
Some binary oprators :

Example: Overloading (Multiplication)

class Number:
def __init__(self, value):
self.value = value

def __mul__(self, other):


return Number(self.value * other.value)

n1 = Number(3)
n2 = Number(4)

PLC142 (Python): Unit-4(OOPs) 17


n3 = n1 * n2
print(n3.value) # Output: 12

2️⃣ Comparison Operators ( == , != , > , < , >= , <= )


These operators help compare objects.

Some comparison operators :

Example: Overloading == (Equality)

class Student:
def __init__(self, name, marks):
self.name = name
self.marks = marks

def __eq__(self, other):


return self.marks == other.marks

s1 = Student("Alice", 90)
s2 = Student("Bob", 90)

print(s1 == s2) # Output: True

📌 Now == compares the marks of students instead of their memory addresses.

2️⃣ Unary Operators Overloading :


Some unary operators :

PLC142 (Python): Unit-4(OOPs) 18


Example: Overloading the - Operator

class Number:
def __init__(self, value):
self.value = value

def __neg__(self): # Overloading - operator


return Number(-self.value)

n1 = Number(7)
n2 = -n1 # Calls __neg__

print(n2.value) # Output: -7

📌Explain:
🔹 The method in the class defines how the operator behaves for instances of the class. When
__neg__ Number - -n1 is
called, it invokes and returns a new
__neg__ object with the negated value.
Number

🔹 creates a new object with


n2 = -n1 Number, and outputs . The original object ( ) remains
value = -7 print(n2.value) -7 n1

unchanged.

📌 Summary
✅ Operator Overloading allows us to change how operators behave for custom objects.
✅ Python provides special methods like , , and
__add__() for overloading.
__mul__() __eq__()

✅ Binary operators like , , , and comparison operators like , can be overloaded.


+ - * == >

✅ Unary Operators like , , and can be overloaded to perform actions on a single operand.
- + ~

✅ This makes our code cleaner, more readable, and easier to understand.
Now, your objects can behave just like numbers and strings! 🚀

## these 2 refer textbook as well :

📌9️⃣ Sameness and Copying in Python OOP


1️⃣ What is Sameness?
In Python, sameness refers to whether two objects are equal in value or refer to the same memory location.
There are two ways to check sameness:

PLC142 (Python): Unit-4(OOPs) 19


✔ Equality ( == ) → Compares values of objects.

✔ Identity ( is ) → Compares memory addresses of objects.

2️⃣ Demonstrating Sameness in Python


🔹 Example: Equality ( == ) vs Identity ( is )

class Car:
def __init__(self, brand):
self.brand = brand

car1 = Car("BMW")
car2 = Car("BMW")
car3 = car1 # ✅ car3 refers to the same object as car1
print(car1 == car2) # ✅ True (Same brand value)
print(car1 is car2) # ❌ False (Different objects in memory)
print(car1 is car3) # ✅ True (Same memory location)

📌 Explanation:
✔ → ✅ True, because both have the same brand.
car1 == car2

✔ → ❌ False, because they are different objects.


car1 is car2

✔ → ✅ True, because
car1 is car3 is just another reference to
car3 car1 .

3️⃣ Why is This Important?


📌 Understanding sameness helps in:
✔ Avoiding unexpected modifications when objects share the same reference.
✔ Preventing unintended changes in complex programs.
✔ Ensuring efficient memory management by controlling object references.
4️⃣ What is Copying in Python?
Copying means creating a duplicate of an object so that changes in one object do not affect the other.

5️⃣ Types of Copying


Copy Type Description

Assignment ( = ) Both variables point to the same object (no real copy).

Shallow Copy ( copy() ) Creates a new object but references same inner objects.

Deep Copy ( deepcopy() ) Creates a fully independent copy, including inner objects.

6️⃣ Example: Assignment ( = ) Does Not Create a Copy

car1 = Car("BMW")
car2 = car1 # ❌ Not a real copy, both point to the same object
car2.brand = "Audi"
print(car1.brand) # Output: Audi (Unexpected change!)

📌 Problem:
✔ means both refer to the same object.
car2 = car1

✔ Changing also affects


car2.brand , because no real copy was made.
car1

PLC142 (Python): Unit-4(OOPs) 20


7️⃣ Example: Creating a Real Copy Using copy()

import copy

car1 = Car("BMW")
car2 = copy.copy(car1) # ✅ Shallow copy
car2.brand = "Audi"
print(car1.brand) # Output: BMW (Original remains unchanged)
print(car2.brand) # Output: Audi (Modified copy)

📌 Explanation:
✔ creates a new object.
copy.copy(car1)

✔ Now modifying does not affect


car2 car1 , because they are independent.

8️⃣ Example: Deep Copy ( deepcopy() )


If an object has nested objects, a shallow copy may not be enough.

class Garage:
def __init__(self, cars):
self.cars = cars

garage1 = Garage([Car("BMW"), Car("Audi")])


garage2 = copy.deepcopy(garage1) # ✅
Fully independent copy

garage2.cars[0].brand = "Tesla"
print(garage1.cars[0].brand) # Output: BMW (Original unchanged)
print(garage2.cars[0].brand) # Output: Tesla (Modified copy)

📌 Explanation:
✔ copy.deepcopy(garage1) creates a fully independent copy.
✔ Changing garage2 does not affect garage1 , unlike a shallow copy.

9️⃣ Real-Life Applications of Sameness and Copying


📌 Why does this matter in real-world programming?
✔ Avoid accidental modifications when working with multiple objects.
✔ Cloning objects safely in applications like gaming, simulations, and AI.
✔ Efficient memory usage by copying only when necessary.
🔟 Summary
✅ Sameness refers to whether objects are equal ( ) or identical ( ).
== is

✅ Assignment ( ) does not create a real copy; both variables point to the same object.
=

✅ Shallow copy ( ) creates a new object but shares references for inner objects.
copy()

✅ Deep copy ( ) creates a fully independent object.


deepcopy()

✅ Understanding sameness and copying helps avoid unintended changes and improves memory efficiency.
📌🔟 Pure Functions and Generalization
1️⃣ What is a Pure Function?
PLC142 (Python): Unit-4(OOPs) 21
A pure function is a function that:
✔ Does not modify its input values.
✔ Has no side effects (does not change global variables, files, or external data).
✔ Always returns the same output for the same input.
Example of a Pure Function

def square(n):
return n * n # Always returns the same result for the same input

print(square(4)) # Output: 16
print(square(4)) # Output: 16 (Same input, same output)

🔹 This function does not modify anything outside itself, making it pure.
2️⃣ What is a Modifier Function? (Opposite of Pure Functions)
A modifier function changes its input instead of returning a new value.

Example of a Modifier Function

def double_list(nums):
for i in range(len(nums)):
nums[i] *= 2 # Modifies the original list

my_list = [2, 4, 6]
double_list(my_list)
print(my_list) # Output: [4, 8, 12] (Original list changed)

🔸 This function modifies my_list , so it is not pure.

3️⃣ What is Generalization?


Generalization means making a function more flexible by using parameters instead of fixed values.

Example Without Generalization (Specific Function)

def greet_john():
print("Hello, John!")

greet_john() # Only works for John

Example With Generalization (Flexible Function)

def greet(name): # Now works for any name!


print(f"Hello, {name}!")

greet("Alice") # Output: Hello, Alice!


greet("Bob") # Output: Hello, Bob!

🔹 Now, this function can greet anyone instead of just John!


4️⃣ Why Are These Concepts Important?
✅ Pure functions make code predictable and easy to debug.
✅ Generalization makes code flexible and reusable.
✅ Using both together leads to better programming practices.
PLC142 (Python): Unit-4(OOPs) 22
🔚 Conclusion
1️⃣ Pure functions don’t modify data; they return new values.
2️⃣ Modifier functions change the original data.
3️⃣ Generalization makes functions work for many cases, not just one.
🚀 Now you understand Pure Functions and Generalization in a simple way! 😃

-Made by Vijay

PLC142 (Python): Unit-4(OOPs) 23

You might also like