0% found this document useful (0 votes)
1 views15 pages

Module 5 Python 1BPLC205B

Uploaded by

truptisr2008
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)
1 views15 pages

Module 5 Python 1BPLC205B

Uploaded by

truptisr2008
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

MODULE 5 PYTHON PROGRAMMING (1BPLC205B)

Syllabus

Object oriented programming: Objects are mutable, Sameness, Copying.


Inheritance: Pure functions ,Modifiers, Generalization, Operator
Overloading, Polymorphism.
Exceptions: Catching Exceptions, Raising your own exceptions.

1
Page

MAHESH KANJIKAR , ASST. PROFESSOR, CSE(AIML), BKIT, BHALKI


MODULE 5 PYTHON PROGRAMMING (1BPLC205B)

OBJECT ORIENTED PROGRAMMING

Objects Are Mutable


Objects are mutable in Python by default (with a few exceptions).
When we say an object is mutable, it means you can change its internal data (its attributes)
or its state after it has been created, without changing its actual identity in memory.
Are all objects in Python mutable?
No. Python divides its built-in types into two categories:
• Mutable Objects: Custom classes (like our Account class), Lists, Dictionaries,
and Sets.
• Immutable Objects: Strings, Integers, Floats, Booleans, and Tuples. If you
"change" an integer or a string, Python actually destroys the old one and creates a
brand-new object elsewhere in memory.

We can prove an object is mutable by checking its unique memory address using Python's
built-in id() function. If we modify an attribute, the data changes, but the id() stays exactly
the same.
Example:
class Account:
def __init__(self, owner, balance):
[Link] = owner
[Link] = balance

# 1. Create a new object


my_account = Account("Alice", 500)
print(f"Original Balance: ${my_account.balance}")
print(f"Memory ID before change: {id(my_account)}")

print("-" * 30)

# 2. Modify the attribute directly (Mutation)


my_account.balance = 750
print(f"New Balance: ${my_account.balance}")
print(f"Memory ID after change: {id(my_account)}")
2
Page

MAHESH KANJIKAR , ASST. PROFESSOR, CSE(AIML), BKIT, BHALKI


MODULE 5 PYTHON PROGRAMMING (1BPLC205B)

Sameness In Python
When talking about sameness in Python, it means understanding the difference between
two concepts that sound identical but mean completely different things in memory:
1. Equality (==): Do these two objects contain the same data? (Value)
2. Identity (is): Are these two variables pointing to the exact same object in memory?
(Location)
This distinction is crucial because custom objects are mutable. Two objects can look
exactly the same on the outside while being completely different entities under the hood.

NOTE :
 Use == when you care about the content (e.g., checking if a user entered the correct
password or if two vectors have the same coordinates).
 Use is when you care about the exact object identity. In practice, is is most commonly
used when checking if something is None (e.g., if value is None:).

Understanding == vs is with Code


Let's create two distinct instances of a Dog class with the exact same data to see how
Python evaluates "sameness."
Example:
class Rectangle:
# ...
width =0
height=0
x=0
y=0
def grow(self, delta_width, delta_height):
""" Grow (or shrink) this object by the deltas """
[Link] += delta_width
[Link] += delta_height

print([Link])
print([Link])
3

def move(self, dx, dy):


Page

MAHESH KANJIKAR , ASST. PROFESSOR, CSE(AIML), BKIT, BHALKI


MODULE 5 PYTHON PROGRAMMING (1BPLC205B)

""" Move this object by the deltas """


self.x += dx
self.y += dy
print(self.x)
print(self.y)

obj = Rectangle()
[Link](11,22)
[Link](33,44)

OUTPUT
11
22
33
44

Copying In Python
Because Python custom objects are mutable and variables only store
references (pointers) to objects in memory, copying is not as simple as writing
object2 = object1.
If you want to duplicate an object so that changes to the new one don't ruin
the original, you need to use Python’s built-in copy module. There are two
ways to do this: Shallow Copy and Deep Copy.
1. Shallow Copy ([Link]())
A shallow copy creates a new outer object, but if that object contains nested
mutable objects (like a list inside a class), it copy-pastes the references to
those nested objects rather than duplicating them.
2. Deep Copy ([Link]())
A deep copy completely duplicates everything. It creates a new outer object
and recursively clones every single nested object inside it. The two objects
become 100% independent.
4
Page

MAHESH KANJIKAR , ASST. PROFESSOR, CSE(AIML), BKIT, BHALKI


MODULE 5 PYTHON PROGRAMMING (1BPLC205B)

INHERITANCE
Pure Function
A function is called pure function if it always returns the same result for same
argument values and it has no side effects like modifying an argument (or
global variable) or outputting something. The only result of calling a pure
function is the return value. Examples of pure functions are strlen(), pow(),
sqrt() etc. Examples of impure functions are printf(), rand(), time(), etc.
Advantages Of Pure Function
• Predictable: Same inputs always give the same output.
• Easy to test: No external setup or cleanup needed.
• Bug-free concurrency: Safe to run on multiple threads.
• Cacheable: Results can be stored to speed up code.
• Reusable: Completely independent and easy to move around.
• Easier to read: Everything the function does is visible.
Pure Function Example
The following function is pure because it only depends on its inputs and does
not change anything outside itself.
Example:
def multiply(x, y):
return x * y

# Always returns 20 when given 4 and 5


print(multiply(4, 5))

NOTE : It relies solely on its parameters x and y and does nothing except
return their product.

Modifiers in Python
The Python access modifiers are used to restrict access to class members
(i.e., variables and methods) from outside the class.
There are three types of access modifiers namely
• Public members − A class member is said to be public if it can be
5

accessed from anywhere in the program.


Page

MAHESH KANJIKAR , ASST. PROFESSOR, CSE(AIML), BKIT, BHALKI


MODULE 5 PYTHON PROGRAMMING (1BPLC205B)

• Protected members − They are accessible from within the class as


well as by classes derived from that class.
• Private members − They can be accessed from within the class only.

Example:
class Employee:
def __init__(self, name, age, salary):
[Link] = name # Public: accessible anywhere
self._age = age # Protected: accessible in subclasses
self.__salary = salary # Private: accessible only in Employee class

# Public method to show private data


def show_salary(self):
return self.__salary

class Manager(Employee):
def show_details(self):
# Can access public and protected attributes
print(f"Name: {[Link]}, Age: {self._age}")

# Cannot access private attribute directly


# print(self.__salary) # This would raise an AttributeError

# --- Testing the Behavior ---

emp = Employee("Alice", 30, 80000)

# 1. Accessing Public
print([Link]) # Output: Alice (Works fine)

# 2. Accessing Protected
print(emp._age) # Output: 30 (Works, but breaks convention)

# 3. Accessing Private
# print(emp.__salary) # Error! AttributeError

# 4. Accessing Private via Public Method


print(emp.show_salary()) # Output: 80000
6
Page

MAHESH KANJIKAR , ASST. PROFESSOR, CSE(AIML), BKIT, BHALKI


MODULE 5 PYTHON PROGRAMMING (1BPLC205B)

# 5. Accessing Private via Name Mangling


print(emp._Employee__salary) # Output: 80000 (Works, but highly
discouraged)

Key Rules to Remember


• Public: These attributes can be used freely everywhere.
• Protected (_): This naming convention signals to other programmers
that the attribute is intended for internal use within the class or its
subclasses.
• Private (__): This triggers name mangling, which changes the variable
name internally to protect it from accidental overrides or direct external
access.

Generalization in Python
Generalization in Object-Oriented Programming (OOP) is the process of
extracting shared features (attributes and methods) from specific classes and
combining them into a broader, more abstract superclass (parent class).
In Python, generalization is implemented directly through inheritance. It
allows you to write reusable code by moving common behaviours up to a base
class, while specific behaviours remain in the subclasses (child classes).
Example:
# Superclass (Generalized Class)
class Vehicle:
def __init__(self, brand, model):
[Link] = brand
[Link] = model

def start_engine(self):
return f"The engine of the {[Link]} {[Link]} is now running."

# Subclass (Specialized Class)


class Car(Vehicle):
7
Page

def open_trunk(self):

MAHESH KANJIKAR , ASST. PROFESSOR, CSE(AIML), BKIT, BHALKI


MODULE 5 PYTHON PROGRAMMING (1BPLC205B)

return "Trunk is now open."

# Subclass (Specialized Class)


class Motorcycle(Vehicle):
def pop_wheelie(self):
return "The motorcycle is doing a wheelie!"

# --- Testing Generalization ---


my_car = Car("Toyota", "Corolla")
my_bike = Motorcycle("Yamaha", "R1")

# Both objects use the generalized method from the Parent class
print(my_car.start_engine()) # Output: The engine of the Toyota Corolla is
now running.
print(my_bike.start_engine()) # Output: The engine of the Yamaha R1 is now
running.

# Each object retains its specialized methods


print(my_car.open_trunk()) # Output: Trunk is now open.

Operator Overloading
Operator Overloading allows you to redefine how built-in operators
(like +, -, *, <, ==) behave when used with your custom objects.
In Python, this is achieved by overriding special, predefined methods
called magic methods (or dunder methods), which always begin and end
with double underscores (e.g., __add__).

Common Magic Methods for Overloading

Category Operator Magic Method

Arithmetic + __add__(self, other)


8
Page

MAHESH KANJIKAR , ASST. PROFESSOR, CSE(AIML), BKIT, BHALKI


MODULE 5 PYTHON PROGRAMMING (1BPLC205B)

- __sub__(self, other)

* __mul__(self, other)

Comparison < __lt__(self, other)

== __eq__(self, other)

>= __ge__(self, other)

String print() __str__(self)


Representation

Rules to Remember
• No New Operators: You cannot create entirely new operators
(like **~**); you can only overload existing ones.
• The other Parameter: The magic method takes self (the object on the
left) and other (the object on the right).
• Polymorphism: Operator overloading is a core form of polymorphism,
allowing a single operator interface to handle different data types.

Example:
class Number:
def __init__(self, value):
[Link] = value

def __add__(self, other): # +


return Number([Link] + [Link])

def __sub__(self, other): # -


return Number([Link] - [Link])

def __mul__(self, other): # *


return Number([Link] * [Link])

def __eq__(self, other): # ==


return [Link] == [Link]
9
Page

MAHESH KANJIKAR , ASST. PROFESSOR, CSE(AIML), BKIT, BHALKI


MODULE 5 PYTHON PROGRAMMING (1BPLC205B)

def __lt__(self, other): # <


return [Link] < [Link]

def __str__(self):
return str([Link])

# Testing the operators


num1 = Number(10)
num2 = Number(5)

print(num1 + num2) # Addition


print(num1 - num2) # Subtraction
print(num1 * num2) # Multiplication
print(num1 == num2) # Equality
print(num1 < num2) # Less than

Polymorphism in Python
Polymorphism means "many forms". It refers to the ability of an entity (like
a function or object) to perform different actions based on the context.
Technically, Polymorphism allows same method, function or operator to
behave differently depending on object it is working with. This makes code
more flexible and reusable.
Types of Polymorphism
Polymorphism refers to ability of the same method or operation to behave
differently based on object or context. It mainly includes
• Compile-time
• Runtime polymorphism.
1. Compile-time Polymorphism
Compile-time polymorphism means deciding which method or operation to
run during compilation, usually through method or operator overloading.
Languages like Java or C++ support this. But Python doesn’t because it’s
dynamically typed it resolves method calls at runtime, not during
compilation. So, true method overloading isn’t supported, though similar
behavior can be achieved using default or variable arguments.
Example: This code demonstrates method overloading using default and
10

variable-length arguments. The multiply() method works with different


numbers of inputs, mimicking compile-time polymorphism.
Page

MAHESH KANJIKAR , ASST. PROFESSOR, CSE(AIML), BKIT, BHALKI


MODULE 5 PYTHON PROGRAMMING (1BPLC205B)

class Calculator:
def multiply(self, a=1, b=1, *args):
result = a * b
for num in args:
result *= num
return result

# Create object
calc = Calculator()

# Using default arguments


print([Link]())
print([Link](4))

# Using multiple arguments


print([Link](2, 3))
print([Link](2, 3, 4))

Output
1
4
6
24

2. Runtime Polymorphism (Overriding)


Runtime polymorphism means that the behavior of a method is decided while
program is running, based on the object calling it. This happens
through Method Overriding a child class provides its own version of a
method already defined in the parent class.
Example: This code shows runtime polymorphism using method overriding.
The sound() method is defined in base class Animal and overridden in Dog
11

and Cat. At runtime, correct method is called based on object's class.


Page

class Calculator:

MAHESH KANJIKAR , ASST. PROFESSOR, CSE(AIML), BKIT, BHALKI


MODULE 5 PYTHON PROGRAMMING (1BPLC205B)

def multiply(self, a=1, b=1, *args):


result = a * b
for num in args:
result *= num
return result

# Create object
calc = Calculator()

# Using default arguments


print([Link]())
print([Link](4))

# Using multiple arguments


print([Link](2, 3))
print([Link](2, 3, 4))
Difference Between Pure Functions And Modifiers

Feature Pure Function Modifier (Mutator)

Original Left completely Modified/Mutated in-place.


Arguments unchanged.

Return Returns a new Usually returns None (or the


Value object/result. modified object).

Side Effects None. Yes (changes state outside


itself).

Memory Higher (creates new Lower (reuses existing


Usage copies). memory).

Best For Functional Object-oriented programming,


programming, parallel performance-critical code with
12

processing, debugging large datasets.


ease.
Page

MAHESH KANJIKAR , ASST. PROFESSOR, CSE(AIML), BKIT, BHALKI


MODULE 5 PYTHON PROGRAMMING (1BPLC205B)

EXCEPTION HANDLING

Exception handling in Python refers to managing runtime errors that


may occur during the execution of a program. In Python, exceptions are raised
when errors or unexpected situations arise during program execution, such as
division by zero, trying to access a file that does not exist, or attempting to
perform an operation on incompatible data types.
Python provides two very important features to handle any unexpected error
in your Python programs and to add debugging capabilities in them −
Syntax of Exception Handling
Python provides four main keywords for handling exceptions: try, except,
else and finally each plays a unique role. Let's see syntax:
try:
# Code
except SomeException:
# Code
else:
# Code
finally:
# Code
• try: Runs the risky code that might cause an error.
• except: Catches and handles the error if one occurs.
• else: Executes only if no exception occurs in try.
• finally: Runs regardless of what happens useful for cleanup tasks like
closing files.

Example: This code attempts division and handles errors gracefully using
try-except-else-finally.
try:
n=0
res = 100 / n

except ZeroDivisionError:
print("You can't divide by zero!")
13
Page

MAHESH KANJIKAR , ASST. PROFESSOR, CSE(AIML), BKIT, BHALKI


MODULE 5 PYTHON PROGRAMMING (1BPLC205B)

except ValueError:
print("Enter a valid number!")

else:
print("Result is", res)

finally:
print("Execution complete.")

Catching Exceptions
We can handle errors more efficiently by specifying the types of exceptions
we expect. This can make code both safer and easier to debug.
1. Catching Specific Exceptions
Catching specific exceptions makes code to respond to different exception
types differently. It precisely makes your code safer and easier to debug. It
avoids masking bugs by only reacting to the exact problems you expect.
Example: This code handles ValueError and ZeroDivisionError with
different messages.
try:
# This will cause ValueError
x = int("str")
inv = 1 / x # Inverse calculation

except ValueError:
print("Not Valid!")

except ZeroDivisionError:
print("Zero has no inverse!")

Output
Not Valid!
14
Page

MAHESH KANJIKAR , ASST. PROFESSOR, CSE(AIML), BKIT, BHALKI


MODULE 5 PYTHON PROGRAMMING (1BPLC205B)

Explanation: A ValueError occurs because "str" cannot be converted to an


integer. If conversion had succeeded but x were 0, a ZeroDivisionError would
have been caught instead.

Raising an Exceptions
You can raise exceptions in several ways by using the raise statement. The
general syntax for the raise statement is as follows.
Syntax
raise [Exception [, args [, traceback]]]
Here, Exception is the type of exception (for example, NameError)
and argument is a value for the exception argument. The argument is
optional; if not supplied, the exception argument is None.
The final argument, trace back, is also optional (and rarely used in practice),
and if present, is the traceback object used for the exception.
Example
An exception can be a string, a class or an object. Most of the exceptions that
the Python core raises are classes, with an argument that is an instance of the
class. Defining new exceptions is quite easy and can be done as follows –
Syntax
def functionName( level ):
if level < 1:
raise "Invalid level!", level
# The code below to this would not be executed
# if we raise the exception
Note: In order to catch an exception, an "except" clause must refer to the same
exception thrown either class object or simple string. For example, to capture
above exception, we must write the except clause as follows −
try:
Business Logic here...
except "Invalid level!":
Exception handling here...
else:
15

Rest of the code here...


Page

User-Defined Exceptions

MAHESH KANJIKAR , ASST. PROFESSOR, CSE(AIML), BKIT, BHALKI

You might also like