Encapsulation is one of the core principles of Object-Oriented Programming (OOP) in Python. Encapsulation is used to bundle data (attributes) and methods that operate on the data into a single unit, usually a class, and restrict direct access to some components. This tutorial covers everything you need to know about encapsulation in Python, including its purpose, syntax, access modifiers, and real-world examples.
In Python, encapsulation is the practice of bundling data and the methods that operate on the data within a single unit, typically a class. Encapsulation helps protect the data from being accessed directly by external code, allowing controlled access to the internal state of an object. This is done by using access modifiers to limit the visibility of attributes and methods.
Encapsulation provides several benefits:
Python uses naming conventions to define access levels for attributes and methods. Although Python doesn’t have strict access modifiers like private or protected in other languages, it provides the following conventions:
Public members are accessible from anywhere, inside or outside of the class. By default, all attributes and methods in Python are public.
class Car:
    def __init__(self, make, model):
        self.make = make  # Public attribute
        self.model = model  # Public attribute
    def display_info(self):  # Public method
        print(f"Car make: {self.make}, Model: {self.model}")
my_car = Car("Toyota", "Corolla")
print(my_car.make)  # Output: Toyota
print(my_car.model) # Output: Corolla
my_car.display_info()  # Output: Car make: Toyota, Model: Corolla
Protected members are indicated by a single underscore _ prefix. Although still accessible outside the class, the underscore is a convention to signal that these members are intended for internal use only and should not be accessed directly.
class Car:
    def __init__(self, make, model, year):
        self._make = make         # Protected attribute
        self._model = model       # Protected attribute
        self._year = year         # Protected attribute
    def _display_info(self):      # Protected method
        print(f"{self._make} {self._model} ({self._year})")
my_car = Car("Honda", "Civic", 2022)
my_car._display_info()  # Output: Honda Civic (2022)
Private members are indicated by a double underscore __ prefix. These attributes and methods are name-mangled by Python to prevent direct access from outside the class.
class BankAccount:
    def __init__(self, balance):
        self.__balance = balance  # Private attribute
    def deposit(self, amount):
        self.__balance += amount
    def withdraw(self, amount):
        if amount <= self.__balance:
            self.__balance -= amount
            return amount
        else:
            print("Insufficient funds")
    def get_balance(self):
        return self.__balance
account = BankAccount(1000)
account.deposit(200)
print(account.get_balance())  # Output: 1200
__balance, which is accessed only through methods within the class.Getters and setters are methods used to access and modify private attributes while preserving data integrity.
class Student:
    def __init__(self, name, age):
        self.__name = name    # Private attribute
        self.__age = age      # Private attribute
    # Getter for name
    def get_name(self):
        return self.__name
    # Setter for name
    def set_name(self, name):
        self.__name = name
    # Getter for age
    def get_age(self):
        return self.__age
    # Setter for age
    def set_age(self, age):
        if age > 0:
            self.__age = age
        else:
            print("Invalid age")
student = Student("Alice", 20)
print(student.get_name())  # Output: Alice
student.set_age(21)
print(student.get_age())   # Output: 21
get_name() and set_name() provide controlled access to the __name attribute.get_age() and set_age() control access to __age, enforcing constraints on the age value.Encapsulation provides several advantages:
Let’s use encapsulation to create a bank account system where a user can deposit, withdraw, and check the balance.
class BankAccount:
    def __init__(self, account_holder, balance=0):
        self.__account_holder = account_holder  # Private attribute
        self.__balance = balance  # Private attribute
    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount
            print(f"Deposited: {amount}")
        else:
            print("Deposit amount must be positive")
    def withdraw(self, amount):
        if amount > self.__balance:
            print("Insufficient funds")
        else:
            self.__balance -= amount
            print(f"Withdrawn: {amount}")
    def get_balance(self):
        return self.__balance
account = BankAccount("John Doe", 500)
account.deposit(200)              # Output: Deposited: 200
account.withdraw(100)             # Output: Withdrawn: 100
print(account.get_balance())       # Output: 600
Encapsulation is useful for managing employee data, ensuring that sensitive data (like salary) is protected.
class Employee:
    def __init__(self, name, position, salary):
        self.name = name               # Public attribute
        self.position = position       # Public attribute
        self.__salary = salary         # Private attribute
    def get_salary(self):
        return self.__salary
    def set_salary(self, salary):
        if salary > 0:
            self.__salary = salary
        else:
            print("Invalid salary")
# Creating an employee
emp = Employee("Alice", "Manager", 70000)
print(emp.get_salary())  # Output: 70000
emp.set_salary(75000)
print(emp.get_salary())  # Output: 75000
__salary attribute is private, so it is only accessible through get_salary() and set_salary() methods.Encapsulation is a crucial aspect of Object-Oriented Programming in Python, allowing developers to control access to data within a class. By using access modifiers, we can protect sensitive information and enforce constraints on how data is modified. Getters and setters offer a way to control data access while maintaining data integrity.
By mastering encapsulation, you can: