Encapsulation and Abstraction in Python

Loading

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?

  1. Public Members – Accessible from anywhere
  2. Protected Members (_variable) – Should be treated as private (convention)
  3. 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

FeatureEncapsulationAbstraction
DefinitionHides data to prevent modificationHides implementation details and exposes only necessary features
FocusProtecting data from unintended accessSimplifying complex systems
ImplementationPrivate (__variable) and Protected (_variable) membersAbstract classes and methods
UsageUsed for security and data hidingUsed for code reusability and flexibility
ExamplePrivate __balance in a BankAccount classAbstract process_payment() method in a Payment class

Leave a Reply

Your email address will not be published. Required fields are marked *