PYTHON ENCAPSULATION

Learn how encapsulation protects object data, controls access through methods and properties, and helps build secure and maintainable Python applications using real-time examples.

What is Encapsulation?

Encapsulation is an important concept in Object-Oriented Programming (OOP). It means combining data and the methods that operate on that data inside a class, while controlling how the data is accessed or modified.

In Python, encapsulation is commonly implemented using public, protected, and private naming conventions, along with methods, properties, and name mangling.

Public
Direct Access
→
Protected
Internal / Subclass Use
→
Private
Controlled Access

Data Protection

Encapsulation helps control how important object data is accessed and changed.

Public Members

Public attributes and methods can normally be accessed directly from outside the class.

Protected Members

A single underscore indicates that a member is intended for internal or subclass use.

Private Members

Double underscores trigger Python's name-mangling mechanism for stronger access restriction.

1. Basic Encapsulation Example

A class can keep its data and the methods that work with that data together.

Python Code
class Student:
    def __init__(self, name, marks):
        self.name = name
        self.marks = marks

    def show_details(self):
        print("Name:", self.name)
        print("Marks:", self.marks)


student = Student("Rahul", 85)

student.show_details()
Output:
Name: Rahul
Marks: 85

The student data and the methods that operate on that data are grouped inside the Student class.

2. Public Members

By default, attributes and methods in Python are public. They can be accessed directly from outside the class.

Python Code
class Employee:
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary


employee = Employee("Priya", 60000)

print(employee.name)
print(employee.salary)

employee.salary = 65000

print(employee.salary)
Output:
Priya
60000
65000

Because name and salary are public, they can be accessed and modified directly.

3. Protected Members

A single underscore before an attribute or method is a convention indicating that it is intended for internal use or use by subclasses.

Python Code
class Employee:
    def __init__(self, name, salary):
        self.name = name
        self._salary = salary

    def show_salary(self):
        print("Salary:", self._salary)


employee = Employee("Amit", 55000)

employee.show_salary()
print(employee._salary)
Output:
Salary: 55000
55000
Important: Python does not strictly prevent access to a single underscore member. The underscore communicates that the member is intended for internal use.
4. Private Members with __

A double underscore before an attribute causes Python to apply name mangling. This is commonly used when you want to discourage direct access from outside the class.

Python Code
class BankAccount:
    def __init__(self, balance):
        self.__balance = balance

    def show_balance(self):
        print("Balance:", self.__balance)


account = BankAccount(50000)

account.show_balance()
Output:
Balance: 50000

The __balance attribute is intended to be accessed through methods provided by the class.

5. Real-Time Example: Bank Account

Banking systems should not allow arbitrary changes to an account balance. Instead, deposits and withdrawals can be controlled through methods.

Python Code
class BankAccount:
    def __init__(self, account_number, balance):
        self.account_number = account_number
        self.__balance = balance

    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount
            print("Deposited:", amount)

    def withdraw(self, amount):
        if amount > 0 and amount <= self.__balance:
            self.__balance -= amount
            print("Withdrawn:", amount)
        else:
            print("Insufficient balance")

    def get_balance(self):
        return self.__balance


account = BankAccount("SB1001", 50000)

account.deposit(10000)
account.withdraw(15000)

print("Current Balance:", account.get_balance())
Output:
Deposited: 10000
Withdrawn: 15000
Current Balance: 45000

The balance is protected from direct modification through the private __balance attribute.

6. Getter and Setter Methods

Getter and setter methods provide controlled access to private data.

Python Code
class Student:
    def __init__(self, name, marks):
        self.__name = name
        self.__marks = marks

    def get_marks(self):
        return self.__marks

    def set_marks(self, marks):
        if 0 <= marks <= 100:
            self.__marks = marks
        else:
            print("Invalid marks")


student = Student("Sneha", 85)

print("Marks:", student.get_marks())

student.set_marks(92)

print("Updated Marks:", student.get_marks())
Output:
Marks: 85
Updated Marks: 92

The setter validates the value before changing the private attribute.

7. Real-Time Example: Employee Salary

Employee salary is sensitive information. A setter can control whether a new salary value is accepted.

Python Code
class Employee:
    def __init__(self, name, salary):
        self.name = name
        self.__salary = salary

    def get_salary(self):
        return self.__salary

    def set_salary(self, salary):
        if salary >= 0:
            self.__salary = salary
        else:
            print("Salary cannot be negative")


employee = Employee("Rahul", 50000)

print("Salary:", employee.get_salary())

employee.set_salary(60000)

print("Updated Salary:", employee.get_salary())
Output:
Salary: 50000
Updated Salary: 60000
8. Real-Time Example: ATM System

An ATM should control access to sensitive information such as the account balance and PIN.

Python Code
class ATM:
    def __init__(self, pin, balance):
        self.__pin = pin
        self.__balance = balance

    def check_pin(self, entered_pin):
        return entered_pin == self.__pin

    def check_balance(self, entered_pin):
        if self.check_pin(entered_pin):
            print("Balance:", self.__balance)
        else:
            print("Invalid PIN")

    def withdraw(self, entered_pin, amount):
        if not self.check_pin(entered_pin):
            print("Invalid PIN")
        elif amount <= 0:
            print("Invalid amount")
        elif amount > self.__balance:
            print("Insufficient balance")
        else:
            self.__balance -= amount
            print("Withdrawn:", amount)
            print("Remaining Balance:", self.__balance)


atm = ATM(1234, 30000)

atm.check_balance(1234)
atm.withdraw(1234, 5000)
Output:
Balance: 30000
Withdrawn: 5000
Remaining Balance: 25000

The ATM class controls access to both the PIN and balance through methods.

9. Using @property for Encapsulation

Python's @property decorator provides a clean way to create controlled getter and setter behavior.

Python Code
class Product:
    def __init__(self, price):
        self.__price = price

    @property
    def price(self):
        return self.__price

    @price.setter
    def price(self, value):
        if value >= 0:
            self.__price = value
        else:
            print("Price cannot be negative")


product = Product(1000)

print("Price:", product.price)

product.price = 1200

print("Updated Price:", product.price)
Output:
Price: 1000
Updated Price: 1200

The property allows the attribute to be accessed using normal dot notation while keeping validation logic inside the class.

10. Real-Time Example: E-Commerce Product

An online store can use encapsulation to prevent invalid product prices or stock quantities.

Python Code
class Product:
    def __init__(self, name, price, stock):
        self.name = name
        self.__price = price
        self.__stock = stock

    def get_price(self):
        return self.__price

    def get_stock(self):
        return self.__stock

    def update_stock(self, quantity):
        if quantity >= 0:
            self.__stock = quantity
        else:
            print("Stock cannot be negative")


product = Product("Laptop", 55000, 20)

print("Product:", product.name)
print("Price:", product.get_price())
print("Stock:", product.get_stock())

product.update_stock(15)

print("Updated Stock:", product.get_stock())
Output:
Product: Laptop
Price: 55000
Stock: 20
Updated Stock: 15
11. Private Methods

A method beginning with double underscores is name-mangled and is intended to be used internally by the class.

Python Code
class Order:
    def __init__(self, price, tax):
        self.price = price
        self.tax = tax

    def __calculate_total(self):
        return self.price + self.tax

    def show_total(self):
        total = self.__calculate_total()
        print("Total:", total)


order = Order(1000, 180)

order.show_total()
Output:
Total: 1180

The internal calculation is hidden behind the public show_total() method.

12. Real-Time Example: Hospital Patient Records

A hospital application can use encapsulation to control access to patient information.

Python Code
class Patient:
    def __init__(self, name, age, disease):
        self.name = name
        self.age = age
        self.__disease = disease

    def get_disease(self):
        return self.__disease

    def update_disease(self, disease):
        if disease:
            self.__disease = disease

    def show_details(self):
        print("Name:", self.name)
        print("Age:", self.age)
        print("Disease:", self.__disease)


patient = Patient("Amit", 35, "Fever")

patient.show_details()

patient.update_disease("Viral Infection")

patient.show_details()
Output:
Name: Amit
Age: 35
Disease: Fever
Name: Amit
Age: 35
Disease: Viral Infection
13. Name Mangling in Python

Python transforms a private attribute such as __balance into a name containing the class name. This mechanism is called name mangling.

Python Code
class Account:
    def __init__(self, balance):
        self.__balance = balance

    def show_balance(self):
        print("Balance:", self.__balance)


account = Account(25000)

account.show_balance()

print(account._Account__balance)
Output:
Balance: 25000
25000
Important: Name mangling is not a security mechanism. It mainly prevents accidental name conflicts and discourages direct access to internal attributes.
14. Encapsulation with Inheritance

Encapsulated data can also be used inside inherited classes through public or protected methods.

Python Code
class Employee:
    def __init__(self, name, salary):
        self.name = name
        self.__salary = salary

    def get_salary(self):
        return self.__salary


class Manager(Employee):
    def show_salary(self):
        print("Manager Salary:", self.get_salary())


manager = Manager("Priya", 85000)

manager.show_salary()
Output:
Manager Salary: 85000

The child class uses the parent's public method instead of directly accessing the private attribute.

15. Encapsulation with Validation

One of the biggest practical benefits of encapsulation is validating data before storing it.

Python Code
class BankAccount:
    def __init__(self, balance):
        self.__balance = 0
        self.set_balance(balance)

    def get_balance(self):
        return self.__balance

    def set_balance(self, balance):
        if balance >= 0:
            self.__balance = balance
        else:
            print("Invalid balance")


account = BankAccount(10000)

print("Balance:", account.get_balance())

account.set_balance(-5000)

print("Balance:", account.get_balance())
Output:
Balance: 10000
Invalid balance
Balance: 10000

The validation prevents an invalid negative balance from replacing the existing valid value.

16. Complete Real-Time Example: Employee Management

This example combines private data, getter and setter methods, validation, and business logic in one employee management system.

Python Code
class Employee:
    def __init__(self, name, employee_id, salary):
        self.name = name
        self.employee_id = employee_id
        self.__salary = salary

    def get_salary(self):
        return self.__salary

    def set_salary(self, salary):
        if salary >= 0:
            self.__salary = salary
        else:
            print("Invalid salary")

    def give_increment(self, percentage):
        if percentage > 0:
            increment = self.__salary * percentage / 100
            self.__salary += increment
            print("Increment:", increment)

    def show_details(self):
        print("Name:", self.name)
        print("Employee ID:", self.employee_id)
        print("Salary:", self.__salary)


employee = Employee(
    "Rahul",
    "EMP101",
    60000
)

employee.show_details()

employee.set_salary(65000)

print()

employee.give_increment(10)

print()

employee.show_details()
Output:
Name: Rahul
Employee ID: EMP101
Salary: 60000

Increment: 6500.0

Name: Rahul
Employee ID: EMP101
Salary: 71500.0

The salary is encapsulated inside the Employee class. External code interacts with it through controlled methods instead of directly modifying the internal value.

17. Public vs Protected vs Private
Type Syntax Meaning Access
Public name Normal public member. Direct access is allowed.
Protected _name Intended for internal/subclass use. Accessible, but convention says avoid direct external use.
Private __name Name-mangled member. Direct access is discouraged and the original name is transformed.
Remember: Python does not provide Java-style strict private attributes. Its encapsulation model relies on conventions, name mangling, properties, and controlled methods.
18. Important Encapsulation Rules

Rule 1: Keep related data and behavior together

A class should contain the data and methods that logically belong together.

Rule 2: Use private members for internal state

Use double underscores when you want Python's name mangling mechanism to discourage direct access.

Rule 3: Use methods for controlled access

Getter and setter methods can validate or transform values before changing internal state.

Rule 4: Use @property for clean interfaces

Properties allow controlled access while keeping the convenient attribute-style syntax.

Rule 5: Encapsulation is not the same as security

Python's private naming and name mangling are primarily tools for organization, preventing accidental access, and reducing unintended coupling.

Remember: Encapsulation means controlling access to an object's internal state and exposing a clean interface for working with that state.

Python Encapsulation - Real-Time Examples

Protect data. Control access. Build maintainable Python applications.