OBJECT-ORIENTED PROGRAMMING IN PYTHON
Module 5 - 2nd Semester BE
1. Classes and Objects
A class is a blueprint for creating objects. An object is an instance of a class. A class defines
attributes (data) and methods (functions) that objects will have.
Key Concepts
• Class: Blueprint or template for objects
• Object: Instance of a class
• Attributes: Data members that store information
• Methods: Functions defined inside a class
2. Defining Classes and Creating Objects
Basic Syntax
class ClassName:
"""Docstring explaining the class"""
def method_name(self):
pass
Example 1: Student Class
class Student:
def __init__(self, name, roll_no, marks):
[Link] = name
self.roll_no = roll_no
[Link] = marks
def display_info(self):
print(f"Name: {[Link]}, Roll: {self.roll_no}")
def calculate_grade(self):
if [Link] >= 90:
return "A"
elif [Link] >= 75:
return "B"
else:
return "C"
Creating Objects (Instances)
student1 = Student("Alice", 101, 95)
student2 = Student("Bob", 102, 78)
student1.display_info()
print(f"Grade: {student1.calculate_grade()}")
Output:
Name: Alice, Roll: 101
Grade: A
3. The __init__() Constructor Method
The __init__() method is a constructor that is automatically called when an object is created. It
initializes object attributes.
Purpose
• Initialize instance attributes with values
• Set up any necessary state
• Perform initialization operations
Example: Car Class
class Car:
def __init__(self, brand, model, year):
[Link] = brand
[Link] = model
[Link] = year
[Link] = 0
def accelerate(self, amount):
[Link] += amount
print(f"{[Link]}: {[Link]} km/h")
car1 = Car("Honda", "Civic", 2023)
[Link](50)
[Link](30)
Important: The 'self' parameter refers to the object. Python automatically passes it.
4. The __str__() Method
The __str__() method returns a string representation of an object. It is called by print() and str().
Without __str__()
class Person:
def __init__(self, name, age):
[Link] = name
[Link] = age
person1 = Person("John", 25)
print(person1)
# <__main__.Person object at 0x7f8b8c0b5f40>
With __str__()
class Person:
def __init__(self, name, age):
[Link] = name
[Link] = age
def __str__(self):
return f"Person: {[Link]}, Age: {[Link]}"
person1 = Person("John", 25)
print(person1)
# Person: John, Age: 25
Example: Book Class
class Book:
def __init__(self, title, author, pages):
[Link] = title
[Link] = author
[Link] = pages
def __str__(self):
s = f"{[Link]} by {[Link]}"
return s + f" ({[Link]} pages)"
book1 = Book("Python Basics", "Mark Lutz", 500)
print(book1)
5. Instance Methods and Class Methods
Instance Methods
Instance methods operate on instance data. They receive 'self' as first parameter and can
access/modify instance attributes.
Class Methods
Class methods operate on class data. They use @classmethod decorator and receive 'cls' as
first parameter.
Example: Bank Account
class BankAccount:
interest_rate = 0.05
def __init__(self, holder, balance):
[Link] = holder
[Link] = balance
def deposit(self, amount):
[Link] += amount
print(f"Deposited: {amount}")
@classmethod
def change_interest_rate(cls, new_rate):
cls.interest_rate = new_rate
acc = BankAccount("Alice", 1000)
[Link](500)
BankAccount.change_interest_rate(0.07)
6. Built-in Class Attributes
Python provides special attributes for classes. These are accessed using dot notation.
Common Built-in Attributes
• __name__: Name of the class
• __doc__: Documentation string of the class
• __module__: Module name where class is defined
• __bases__: Tuple of base classes
• __dict__: Dictionary containing class attributes
Example
class Employee:
"""Represents an employee"""
company = "TechCorp"
def __init__(self, name, emp_id):
[Link] = name
self.emp_id = emp_id
print(f"Name: {Employee.__name__}")
print(f"Module: {Employee.__module__}")
print(f"Doc: {Employee.__doc__}")
print(f"Bases: {Employee.__bases__}")
7. Public and Private Data Members
Data members are variables that store data. They can be public (accessible anywhere) or
private (accessible only within the class).
Public Data Members
Public members are accessible from anywhere. By convention, they have no prefix.
class Rectangle:
def __init__(self, length, width):
[Link] = length
[Link] = width
def area(self):
return [Link] * [Link]
rect = Rectangle(5, 3)
print([Link]) # 5
print([Link]()) # 15
Private Data Members
Private members are prefixed with __ (double underscore). Accessible only within the class.
class BankAccount:
def __init__(self, account_no, balance):
self.account_no = account_no
self.__balance = balance
def deposit(self, amount):
if amount > 0:
self.__balance += amount
def get_balance(self):
return self.__balance
account = BankAccount("12345", 1000)
print(account.account_no) # Works
print(account.get_balance()) # Works
Protected Data Members
Protected members are prefixed with _ (single underscore). Technically accessible but
discouraged.
class Shape:
def __init__(self, color):
self._color = color
def get_info(self):
return f"Color: {self._color}"
shape = Shape("Red")
print(shape._color) # Accessible but discouraged
8. Pure Functions
A pure function always returns the same output for the same input and has no side effects. It
doesn't modify external state.
Characteristics
• Same input always produces same output
• No side effects
• Doesn't depend on external state
Impure Function (Has Side Effects)
counter = 0
def increment():
global counter
counter += 1
return counter
print(increment()) # 1
print(increment()) # 2 (different output)
Pure Function
def add_numbers(a, b):
return a + b
print(add_numbers(5, 3)) # Always 8
print(add_numbers(5, 3)) # Always 8
Pure Methods in Classes
class Calculator:
def add_pure(self, a, b):
return a + b
def multiply_pure(self, a, b):
return a * b
calc = Calculator()
print(calc.add_pure(5, 3)) # 8
print(calc.multiply_pure(4, 5)) # 20
9. Access Modifiers
Access modifiers control the visibility of class members. They determine which code can access
specific attributes and methods.
Types of Modifiers
• Public: No prefix - Accessible anywhere
• Protected: Single _ prefix - Class & subclasses
• Private: Double __ prefix - Within class only
Example: Using Modifiers
class Student:
def __init__(self, name, roll, gpa):
[Link] = name
self._roll = roll
self.__gpa = gpa
def get_gpa(self):
return self.__gpa
def set_gpa(self, new_gpa):
if 0 <= new_gpa <= 4.0:
self.__gpa = new_gpa
student = Student("John", 101, 3.5)
print([Link]) # Works
print(student.get_gpa()) # Works
10. Inheritance
Inheritance is a mechanism where a derived class inherits properties and methods from a base
class. It promotes code reusability.
Type 1: Single Inheritance
One derived class inherits from one base class.
class Animal:
def __init__(self, name):
[Link] = name
def speak(self):
print(f"{[Link]} makes sound")
class Dog(Animal):
def speak(self):
print(f"{[Link]} barks")
dog = Dog("Buddy")
[Link]() # Buddy barks
Type 2: Multi-level Inheritance
A derived class inherits from another derived class, forming a chain.
class Vehicle:
def start(self):
print("Vehicle starts")
class Car(Vehicle):
def drive(self):
print("Car drives")
class ElectricCar(Car):
def charge(self):
print("Charging...")
tesla = ElectricCar()
[Link]()
[Link]()
[Link]()
Type 3: Multiple Inheritance
A derived class inherits from multiple base classes.
class Flying:
def fly(self):
print("Can fly")
class Swimming:
def swim(self):
print("Can swim")
class Duck(Flying, Swimming):
pass
duck = Duck()
[Link]()
[Link]()
Type 4: Hierarchical Inheritance
Multiple derived classes inherit from a single base class.
class Shape:
def area(self):
pass
class Circle(Shape):
def __init__(self, r):
[Link] = r
def area(self):
return 3.14 * [Link] ** 2
class Square(Shape):
def __init__(self, s):
[Link] = s
def area(self):
return [Link] ** 2
c = Circle(5)
s = Square(4)
print([Link]()) # 78.5
print([Link]()) # 16
The super() Function
The super() function allows you to call methods from the parent class.
class Animal:
def __init__(self, name):
[Link] = name
class Dog(Animal):
def __init__(self, name, breed):
super().__init__(name)
[Link] = breed
dog = Dog("Buddy", "Golden")
print(f"Name: {[Link]}")
print(f"Breed: {[Link]}")
11. Polymorphism
Polymorphism means 'many forms'. It allows the same method name to behave differently
based on the object it's called on.
Method Overriding
A derived class provides a different implementation of a method from the base class.
class Shape:
def area(self):
return 0
class Circle(Shape):
def __init__(self, r):
[Link] = r
def area(self):
return 3.14 * [Link] ** 2
class Square(Shape):
def __init__(self, s):
[Link] = s
def area(self):
return [Link] ** 2
def print_area(shape):
print(f"Area: {[Link]()}")
c = Circle(5)
s = Square(4)
print_area(c)
print_area(s)
Operator Overloading
Operator overloading defines how operators (+, -, *, etc.) work with objects of your class.
Common Operator Methods:
• __add__(): Defines behavior for + operator
• __sub__(): Defines behavior for - operator
• __mul__(): Defines behavior for * operator
• __eq__(): Defines behavior for == operator
• __str__(): String representation
Example: Vector Class
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Vector(self.x+other.x, self.y+other.y)
def __sub__(self, other):
return Vector(self.x-other.x, self.y-other.y)
def __mul__(self, scalar):
return Vector(self.x*scalar, self.y*scalar)
def __str__(self):
return f"({self.x}, {self.y})"
v1 = Vector(2, 3)
v2 = Vector(4, 1)
v3 = v1 + v2
v4 = v1 * 2
print(f"v1 + v2 = {v3}")
print(f"v1 * 2 = {v4}")
Summary and Key Points
Essential Concepts
• Classes are blueprints; objects are instances
• __init__() initializes object attributes
• __str__() provides string representation
• Access modifiers control visibility
• Inheritance enables code reuse
• Polymorphism allows flexible object handling
Best Practices
• Use meaningful class and method names
• Keep classes focused and single-purpose
• Use private attributes to hide implementation
• Provide accessor methods for private data
• Use super() to extend parent functionality
• Always implement __str__() for clarity