![]()
Encapsulation and Abstraction are two fundamental principles of Object-Oriented Programming (OOP) that help in hiding implementation details and protecting data.
Encapsulation – Hides data by restricting direct access to class attributes
Abstraction – Hides complex implementation details and exposes only the necessary features
These concepts enhance security, modularity, and maintainability of the code.
1. Encapsulation in Python
What is Encapsulation?
Encapsulation refers to bundling data and methods into a single unit (class) and restricting access to some attributes to protect data from unintended modifications.
How is Encapsulation Achieved?
- Public Members – Accessible from anywhere
- Protected Members (
_variable) – Should be treated as private (convention) - Private Members (
__variable) – Cannot be accessed directly outside the class
1.1 Public Members
Public attributes and methods can be accessed from anywhere.
class Car:
def __init__(self, brand, model):
self.brand = brand # Public attribute
self.model = model # Public attribute
def show_details(self):
print(f"Car: {self.brand}, Model: {self.model}")
car = Car("Toyota", "Corolla")
print(car.brand) # ✅ Accessible
print(car.model) # ✅ Accessible
car.show_details()
Public members are fully accessible from outside the class.
1.2 Protected Members (_variable)
Protected members should be treated as “semi-private” and not modified directly.
class Laptop:
def __init__(self, brand, price):
self.brand = brand # Public
self._price = price # Protected (convention)
def show_price(self):
print(f"Price: ${self._price}")
laptop = Laptop("Dell", 1000)
print(laptop._price) # Not recommended but accessible
laptop.show_price()
Protected members can still be accessed, but they follow a convention (_variable) to indicate they should not be modified directly.
1.3 Private Members (__variable)
Private members cannot be accessed directly from outside the class.
class BankAccount:
def __init__(self, account_number, balance):
self.account_number = account_number # Public
self.__balance = balance # Private
def deposit(self, amount):
self.__balance += amount
print(f"Deposited: ${amount}, New Balance: ${self.__balance}")
def __apply_fees(self): # Private method
self.__balance -= 5
account = BankAccount("123456789", 1000)
# print(account.__balance) # AttributeError: Cannot access private attribute
account.deposit(500) # Allowed
# Accessing private variable using name mangling
print(account._BankAccount__balance) # ⚠️ Not recommended, but possible
Private members are prefixed with __ (double underscore) to prevent direct access.
Name mangling (_ClassName__variable) allows access but is not recommended.
2. Abstraction in Python
What is Abstraction?
Abstraction means hiding implementation details and exposing only necessary functionality.
It allows defining a blueprint for classes using abstract classes and methods.
Why Use Abstraction?
✅ Hides complexity – Users only see the essential details
✅ Improves modularity – Separates interface from implementation
✅ Encourages code reusability
2.1 Abstract Classes and Methods (Using abc Module)
An abstract class defines a template for other classes.
An abstract method must be implemented in child classes.
Example: Abstract Class
from abc import ABC, abstractmethod
class Vehicle(ABC): # Abstract Class
@abstractmethod
def start(self):
pass # No implementation
@abstractmethod
def stop(self):
pass # No implementation
class Car(Vehicle): # Concrete class
def start(self):
print("Car engine started")
def stop(self):
print("Car engine stopped")
class Bike(Vehicle): # Another concrete class
def start(self):
print("Bike started")
def stop(self):
print("Bike stopped")
car = Car()
car.start() # Output: Car engine started
car.stop() # Output: Car engine stopped
bike = Bike()
bike.start() # Output: Bike started
bike.stop() # Output: Bike stopped
Abstract classes cannot be instantiated directly.
Subclasses must implement all abstract methods from the parent class.
2.2 Abstraction in Real-World Example: Payment Processing
from abc import ABC, abstractmethod
class Payment(ABC): # Abstract class
@abstractmethod
def process_payment(self, amount):
pass
class CreditCardPayment(Payment):
def process_payment(self, amount):
print(f"Processing credit card payment of ${amount}")
class PayPalPayment(Payment):
def process_payment(self, amount):
print(f"Processing PayPal payment of ${amount}")
# Function using abstraction
def make_payment(payment_method, amount):
payment_method.process_payment(amount)
cc = CreditCardPayment()
paypal = PayPalPayment()
make_payment(cc, 100) # Output: Processing credit card payment of $100
make_payment(paypal, 50) # Output: Processing PayPal payment of $50
Why abstraction is useful?
- We define a common interface (
process_payment) in the abstract class. - Different payment methods implement their own versions of
process_payment. - The
make_payment()function works with any Payment subclass.
3. Key Differences Between Encapsulation and Abstraction
| Feature | Encapsulation | Abstraction |
|---|---|---|
| Definition | Hides data to prevent modification | Hides implementation details and exposes only necessary features |
| Focus | Protecting data from unintended access | Simplifying complex systems |
| Implementation | Private (__variable) and Protected (_variable) members | Abstract classes and methods |
| Usage | Used for security and data hiding | Used for code reusability and flexibility |
| Example | Private __balance in a BankAccount class | Abstract process_payment() method in a Payment class |
