Object-Oriented Programming (OOP) is a programming paradigm built around objects, which bundle data (attributes) and functions (methods). Python supports OOP features clearly, allowing programmers to create reusable, maintainable, and modular code.
The core principles of OOP include:
- Classes and Objects
- Inheritance
- Polymorphism
- Encapsulation
Let’s explore these concepts deeply, accompanied by clear explanations of examples.
1. Classes and Objects
A class in Python serves as a blueprint or a template for creating objects. Objects created from a class have attributes (data fields) and methods (behaviors).
Example:
class Student:
# Constructor method to initialize instance variables
def __init__(self, name, student_id, major):
self.name = name
self.student_id = student_id
self.major = major
self.grades = []
# Method to add a grade
def add_grade(self, grade):
self.grades.append(grade)
# Method to calculate average grade
def calculate_average(self):
if self.grades:
return sum(self.grades) / len(self.grades)
else:
return 0.0
# Method to display student information
def info(self):
avg_grade = self.calculate_average()
return f"Student: {self.name}, ID: {self.student_id}, Major: {self.major}, Average Grade: {avg_grade:.2f}"
In the example above, the class Student
has attributes like name
, student_id
, major
, and grades
. It also has methods to handle student behaviors like adding grades and calculating averages.
Creating Objects and Using Them
student1 = Student("Alice", "S001", "Physics")
student1.add_grade(85)
student1.add_grade(90)
student1.add_grade(95)
print(student1.info())
Explanation of the Code:
Here, we create an instance of the Student
class named student1
. We add three grades to this student’s record using the add_grade()
method. Then, we display the student’s information, including the calculated average grade, using the info()
method.
Output:
Student: Alice, ID: S001, Major: Physics, Average Grade: 90.00
2. Inheritance
Inheritance allows a new class (called child or subclass) to derive properties and methods from an existing class (parent or superclass). This approach promotes code reuse and clean organization.
Example of Inheritance:
# Parent class
class Vehicle:
def __init__(self, brand, model):
self.brand = brand
self.model = model
def drive(self):
print(f"The {self.brand} {self.model} is driving.")
# Child class inheriting from Vehicle
class ElectricCar(Vehicle):
def __init__(self, brand, model, battery_capacity):
super().__init__(brand, model) # Calls parent constructor
self.battery_capacity = battery_capacity
# Overriding parent method
def drive(self):
print(f"The electric car {self.brand} {self.model} is silently driving.")
# New method specific to ElectricCar
def charge(self):
print(f"Charging {self.brand} {self.model}'s battery ({self.battery_capacity} kWh).")
Creating Objects and Explanation:
tesla = ElectricCar("Tesla", "Model 3", 75)
tesla.drive() # overridden method
tesla.charge() # child-specific method
In this example, we create an ElectricCar
object named tesla
. It inherits attributes (brand
, model
) and methods (drive
) from the parent Vehicle
class. Additionally, ElectricCar
has a specific attribute (battery_capacity
) and method (charge
). The drive()
method is overridden to reflect the unique behavior of electric vehicles.
Output:
The electric car Tesla Model 3 is silently driving.
Charging Tesla Model 3’s battery (75 kWh).
3. Polymorphism
Polymorphism means “many forms.” It enables objects of different classes to be treated as instances of the same parent class, allowing one interface to represent multiple underlying forms.
Example of Polymorphism:
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
print("Animal makes a sound.")
class Dog(Animal):
def speak(self):
print(f"{self.name} says Woof!")
class Cat(Animal):
def speak(self):
print(f"{self.name} says Meow!")
def animal_sound(animal):
animal.speak()
dog = Dog("Buddy")
cat = Cat("Whiskers")
animal_sound(dog)
animal_sound(cat)
Explanation of the Code:
Here, both Dog
and Cat
inherit from Animal
and override the speak()
method. The function animal_sound()
takes an animal object and calls its speak()
method. Python dynamically decides at runtime which method (Dog
or Cat
) to execute, demonstrating polymorphism clearly.
Output:
Buddy says Woof!
Whiskers says Meow!
4. Encapsulation
Encapsulation bundles data (attributes) and methods that operate on the data within a class. It restricts direct access to some of the object’s components, protecting data integrity and enhancing security.
Example of Encapsulation:
class BankAccount:
def __init__(self, account_holder, balance=0):
self.account_holder = account_holder
self.__balance = balance # private attribute
def deposit(self, amount):
if amount > 0:
self.__balance += amount
def withdraw(self, amount):
if 0 < amount <= self.__balance:
self.__balance -= amount
else:
print("Insufficient funds.")
def get_balance(self):
return self.__balance
Explanation of Encapsulation Example:
In the code above, the __balance
attribute is private (indicated by the double underscore). It cannot be directly accessed or modified from outside the class. Instead, we provide public methods (deposit()
, withdraw()
, and get_balance()
) to control how the balance can be accessed or updated, ensuring data integrity and security.
Usage Example:
account = BankAccount("Alice", 500)
account.deposit(200)
account.withdraw(100)
print(f"Balance: ${account.get_balance()}")
# Direct access will result in error
# print(account.__balance) # This raises an AttributeError
Output:
Balance: $600
- Classes & Objects organize data and behavior.
- Inheritance allows reuse of common properties and methods.
- Polymorphism ensures the same interface for different object types.
- Encapsulation protects data and controls its access.
By mastering these fundamental OOP concepts in Python, you’ll be equipped to create efficient, maintainable, and scalable software.