
Python OOP principles are fundamental concepts that guide how we design and structure object-oriented programs in Python. These principles help us create code that is modular, reusable, and easier to maintain. When you master Python OOP principles, you’ll be able to build complex applications with clean, organized code structures.
The four core Python OOP principles work together to provide a comprehensive framework for object-oriented design. Each principle addresses specific aspects of code organization and behavior, making your Python programs more efficient and professional.
Encapsulation is one of the most important Python OOP principles that focuses on bundling data and methods within a single unit (class) while controlling access to the internal components. This Python OOP principle ensures data security and maintains the integrity of your objects.
In Python, encapsulation is implemented through access modifiers that control the visibility of class attributes and methods. Python uses naming conventions to indicate the intended access level:
name)_age)__salary)class BankAccount:
def __init__(self, account_holder, initial_balance):
self.account_holder = account_holder # Public attribute
self._account_number = "ACC12345" # Protected attribute
self.__balance = initial_balance # Private attribute
def deposit(self, amount):
if amount > 0:
self.__balance += amount
return f"Deposited ${amount}. New balance: ${self.__balance}"
return "Invalid deposit amount"
def withdraw(self, amount):
if 0 < amount <= self.__balance:
self.__balance -= amount
return f"Withdrew ${amount}. Remaining balance: ${self.__balance}"
return "Insufficient funds or invalid amount"
def get_balance(self):
return self.__balance
# Usage example
account = BankAccount("John Doe", 1000)
print(account.account_holder) # Accessible (public)
print(account._account_number) # Accessible but not recommended
# print(account.__balance) # This would raise an AttributeError
print(account.get_balance()) # Proper way to access private data
Inheritance is a fundamental Python OOP principle that allows a class to inherit attributes and methods from another class. This principle promotes code reusability and establishes relationships between classes, making inheritance one of the most powerful Python OOP principles.
Python supports several types of inheritance patterns:
class Vehicle:
def __init__(self, brand, model, year):
self.brand = brand
self.model = model
self.year = year
self.is_running = False
def start_engine(self):
self.is_running = True
return f"{self.brand} {self.model} engine started!"
def stop_engine(self):
self.is_running = False
return f"{self.brand} {self.model} engine stopped!"
class Car(Vehicle):
def __init__(self, brand, model, year, num_doors):
super().__init__(brand, model, year)
self.num_doors = num_doors
self.trunk_open = False
def open_trunk(self):
self.trunk_open = True
return "Trunk is now open"
def close_trunk(self):
self.trunk_open = False
return "Trunk is now closed"
# Usage example
my_car = Car("Toyota", "Camry", 2023, 4)
print(my_car.start_engine()) # Inherited method
print(my_car.open_trunk()) # Child class method
class Flyable:
def __init__(self):
self.can_fly = True
def fly(self):
return "Flying through the sky!"
class Swimmable:
def __init__(self):
self.can_swim = True
def swim(self):
return "Swimming in the water!"
class Duck(Flyable, Swimmable):
def __init__(self, name):
Flyable.__init__(self)
Swimmable.__init__(self)
self.name = name
def quack(self):
return f"{self.name} says: Quack!"
# Usage example
donald = Duck("Donald")
print(donald.fly()) # From Flyable
print(donald.swim()) # From Swimmable
print(donald.quack()) # Duck's own method
Polymorphism is a crucial Python OOP principle that allows objects of different classes to be treated uniformly through a common interface. This Python OOP principle enables the same method name to behave differently based on the object that calls it.
Method overriding is a common implementation of polymorphism where a child class provides a specific implementation of a method that exists in its parent class.
class Animal:
def __init__(self, name):
self.name = name
def make_sound(self):
return f"{self.name} makes a sound"
def move(self):
return f"{self.name} moves around"
class Dog(Animal):
def make_sound(self):
return f"{self.name} barks: Woof!"
def move(self):
return f"{self.name} runs on four legs"
class Bird(Animal):
def make_sound(self):
return f"{self.name} chirps: Tweet!"
def move(self):
return f"{self.name} flies with wings"
class Fish(Animal):
def make_sound(self):
return f"{self.name} makes bubbles"
def move(self):
return f"{self.name} swims with fins"
# Usage example demonstrating polymorphism
animals = [
Dog("Buddy"),
Bird("Tweety"),
Fish("Nemo")
]
for animal in animals:
print(animal.make_sound()) # Same method, different behavior
print(animal.move())
print("-" * 30)
Python allows you to implement polymorphism through operator overloading using special methods (magic methods):
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"Vector({self.x}, {self.y})"
def __eq__(self, other):
return self.x == other.x and self.y == other.y
# Usage example
v1 = Vector(3, 4)
v2 = Vector(1, 2)
print(v1 + v2) # Vector(4, 6)
print(v1 - v2) # Vector(2, 2)
print(v1 * 2) # Vector(6, 8)
print(v1 == v2) # False
Abstraction is a fundamental Python OOP principle that focuses on hiding complex implementation details while exposing only essential features to the user. This Python OOP principle is implemented through abstract classes and methods using Python’s abc module.
from abc import ABC, abstractmethod
class Shape(ABC):
def __init__(self, color):
self.color = color
@abstractmethod
def calculate_area(self):
pass
@abstractmethod
def calculate_perimeter(self):
pass
def get_info(self):
return f"This is a {self.color} shape"
class Rectangle(Shape):
def __init__(self, color, width, height):
super().__init__(color)
self.width = width
self.height = height
def calculate_area(self):
return self.width * self.height
def calculate_perimeter(self):
return 2 * (self.width + self.height)
class Circle(Shape):
def __init__(self, color, radius):
super().__init__(color)
self.radius = radius
def calculate_area(self):
return 3.14159 * self.radius ** 2
def calculate_perimeter(self):
return 2 * 3.14159 * self.radius
# Usage example
rectangle = Rectangle("blue", 5, 3)
circle = Circle("red", 4)
print(f"Rectangle area: {rectangle.calculate_area()}")
print(f"Rectangle perimeter: {rectangle.calculate_perimeter()}")
print(f"Circle area: {circle.calculate_area():.2f}")
print(f"Circle perimeter: {circle.calculate_perimeter():.2f}")
Here’s a comprehensive example that demonstrates all Python OOP principles working together in a practical library management system:
from abc import ABC, abstractmethod
from datetime import datetime, timedelta
# Abstract base class demonstrating Abstraction
class LibraryItem(ABC):
def __init__(self, title, item_id):
self.title = title
self.item_id = item_id
self._is_available = True # Protected attribute (Encapsulation)
self.__date_added = datetime.now() # Private attribute (Encapsulation)
@abstractmethod
def get_item_type(self):
pass
@abstractmethod
def get_rental_period(self):
pass
def is_available(self):
return self._is_available
def check_out(self):
if self._is_available:
self._is_available = False
return f"{self.title} has been checked out"
return f"{self.title} is not available"
def return_item(self):
self._is_available = True
return f"{self.title} has been returned"
# Inheritance: Book inherits from LibraryItem
class Book(LibraryItem):
def __init__(self, title, item_id, author, pages):
super().__init__(title, item_id)
self.author = author
self.pages = pages
def get_item_type(self):
return "Book"
def get_rental_period(self):
return 14 # days
# Method specific to Book class
def get_reading_time(self):
# Assume 250 words per page, 200 words per minute reading speed
return f"Estimated reading time: {(self.pages * 250) // 200} minutes"
# Inheritance: DVD inherits from LibraryItem
class DVD(LibraryItem):
def __init__(self, title, item_id, director, duration):
super().__init__(title, item_id)
self.director = director
self.duration = duration # in minutes
def get_item_type(self):
return "DVD"
def get_rental_period(self):
return 7 # days
# Method specific to DVD class
def get_duration_hours(self):
return f"Duration: {self.duration // 60}h {self.duration % 60}m"
# Inheritance: Magazine inherits from LibraryItem
class Magazine(LibraryItem):
def __init__(self, title, item_id, issue_number, publication_date):
super().__init__(title, item_id)
self.issue_number = issue_number
self.publication_date = publication_date
def get_item_type(self):
return "Magazine"
def get_rental_period(self):
return 3 # days
# Library Management System demonstrating all OOP principles
class Library:
def __init__(self, name):
self.name = name
self.__items = [] # Private list (Encapsulation)
self.__members = {} # Private dictionary (Encapsulation)
def add_item(self, item):
self.__items.append(item)
return f"{item.get_item_type()} '{item.title}' added to library"
def register_member(self, member_id, name):
self.__members[member_id] = {
'name': name,
'borrowed_items': []
}
return f"Member {name} registered successfully"
# Polymorphism: This method works with any LibraryItem subclass
def display_items(self):
print(f"\n=== {self.name} Catalog ===")
for item in self.__items:
status = "Available" if item.is_available() else "Checked Out"
print(f"{item.get_item_type()}: {item.title} - {status}")
print(f" Rental Period: {item.get_rental_period()} days")
# Polymorphism: Different behavior based on object type
if isinstance(item, Book):
print(f" Author: {item.author}")
print(f" {item.get_reading_time()}")
elif isinstance(item, DVD):
print(f" Director: {item.director}")
print(f" {item.get_duration_hours()}")
elif isinstance(item, Magazine):
print(f" Issue: #{item.issue_number}")
print("-" * 40)
def borrow_item(self, member_id, item_id):
if member_id not in self.__members:
return "Member not found"
for item in self.__items:
if item.item_id == item_id:
if item.is_available():
result = item.check_out()
self.__members[member_id]['borrowed_items'].append(item)
return result
return f"{item.title} is currently not available"
return "Item not found"
def return_item(self, member_id, item_id):
if member_id not in self.__members:
return "Member not found"
member_items = self.__members[member_id]['borrowed_items']
for i, item in enumerate(member_items):
if item.item_id == item_id:
result = item.return_item()
member_items.pop(i)
return result
return "Item not found in member's borrowed list"
# Complete usage example with all dependencies and imports
def main():
# Create library
city_library = Library("City Central Library")
# Create different types of library items (demonstrating inheritance)
book1 = Book("Python Programming", "B001", "John Smith", 350)
book2 = Book("Data Structures", "B002", "Jane Doe", 420)
dvd1 = DVD("Python Tutorial Series", "D001", "Tech Academy", 180)
magazine1 = Magazine("Python Weekly", "M001", 45, "2024-01-15")
# Add items to library
print(city_library.add_item(book1))
print(city_library.add_item(book2))
print(city_library.add_item(dvd1))
print(city_library.add_item(magazine1))
# Register library members
print(city_library.register_member("MEM001", "Alice Johnson"))
print(city_library.register_member("MEM002", "Bob Wilson"))
# Display all items (demonstrating polymorphism)
city_library.display_items()
# Borrow items
print("\n=== Borrowing Items ===")
print(city_library.borrow_item("MEM001", "B001"))
print(city_library.borrow_item("MEM002", "D001"))
# Display items after borrowing
city_library.display_items()
# Return items
print("\n=== Returning Items ===")
print(city_library.return_item("MEM001", "B001"))
# Final display
city_library.display_items()
if __name__ == "__main__":
main()
Expected Output:
Book 'Python Programming' added to library
Book 'Data Structures' added to library
DVD 'Python Tutorial Series' added to library
Magazine 'Python Weekly' added to library
Member Alice Johnson registered successfully
Member Bob Wilson registered successfully
=== City Central Library Catalog ===
Book: Python Programming - Available
Rental Period: 14 days
Author: John Smith
Estimated reading time: 437 minutes
----------------------------------------
Book: Data Structures - Available
Rental Period: 14 days
Author: Jane Doe
Estimated reading time: 525 minutes
----------------------------------------
DVD: Python Tutorial Series - Available
Rental Period: 7 days
Director: Tech Academy
Duration: 3h 0m
----------------------------------------
Magazine: Python Weekly - Available
Rental Period: 3 days
Issue: #45
----------------------------------------
=== Borrowing Items ===
Python Programming has been checked out
Python Tutorial Series has been checked out
=== City Central Library Catalog ===
Book: Python Programming - Checked Out
Rental Period: 14 days
Author: John Smith
Estimated reading time: 437 minutes
----------------------------------------
Book: Data Structures - Available
Rental Period: 14 days
Author: Jane Doe
Estimated reading time: 525 minutes
----------------------------------------
DVD: Python Tutorial Series - Checked Out
Rental Period: 7 days
Director: Tech Academy
Duration: 3h 0m
----------------------------------------
Magazine: Python Weekly - Available
Rental Period: 3 days
Issue: #45
----------------------------------------
=== Returning Items ===
Python Programming has been returned
=== City Central Library Catalog ===
Book: Python Programming - Available
Rental Period: 14 days
Author: John Smith
Estimated reading time: 437 minutes
----------------------------------------
Book: Data Structures - Available
Rental Period: 14 days
Author: Jane Doe
Estimated reading time: 525 minutes
----------------------------------------
DVD: Python Tutorial Series - Checked Out
Rental Period: 7 days
Director: Tech Academy
Duration: 3h 0m
----------------------------------------
Magazine: Python Weekly - Available
Rental Period: 3 days
Issue: #45
----------------------------------------