Encapsulation in Python
Encapsulation is one of the core principles of Object-Oriented Programming. It protects the internal data of a class by controlling how it is accessed or modified. This helps build secure, organized, and well-structured programs.
What Is Encapsulation?
Encapsulation refers to wrapping data (variables) and methods (functions) into a single unit — a class. It restricts direct access to certain components, preventing accidental modifications. This ensures your program behaves in a predictable and safe way.
Why Use Encapsulation?
Encapsulation helps maintain data integrity inside objects. It allows developers to hide unnecessary implementation details and expose only what is needed. This provides control, security, and a cleaner interface for interacting with objects.
Types of Encapsulation in Python
Python provides three main levels of access control: Public, Protected, and Private. Although Python uses naming conventions instead of strict rules, these conventions are widely followed.
1. Public Members
Public variables and methods can be accessed freely from anywhere. Most class attributes are public by default unless specified otherwise.
class Person:
def __init__(self):
self.name = "Alex" # public
p = Person()
print(p.name)
2. Protected Members
Protected members use a single underscore prefix (_value).
They indicate that the attribute is intended for internal use, but still accessible if needed.
class Person:
def __init__(self):
self._age = 25 # protected
p = Person()
print(p._age)
Protected variables should be used carefully but are not strictly hidden.
3. Private Members
Private members use a double underscore prefix (__value).
They cannot be accessed directly from outside the class, making them useful for sensitive data.
class Person:
def __init__(self):
self.__salary = 5000 # private
p = Person()
# print(p.__salary) # Error
Private attributes can only be accessed through special methods inside the class.
Getter and Setter Methods
Getters allow you to read private data, while setters allow controlled updates. This ensures that values change only when rules are followed, improving data safety.
class BankAccount:
def __init__(self):
self.__balance = 1000
def get_balance(self):
return self.__balance
def set_balance(self, amount):
if amount > 0:
self.__balance = amount
acct = BankAccount()
print(acct.get_balance())
Setters help prevent invalid updates, such as negative amounts.
Name Mangling
Private variables are internally renamed using name mangling. This prevents accidental access while still making testing and debugging possible.
class Demo:
def __init__(self):
self.__value = 10
d = Demo()
print(d._Demo__value) # accessing private using name mangling
Although name mangling allows access, it should be used only when necessary.
Real-World Example
Encapsulation is widely used in financial systems, software security, and user-data protection. It ensures sensitive information like passwords, balances, and personal details stay controlled.
class User:
def __init__(self, password):
self.__password = password
def verify(self, pwd):
return pwd == self.__password
u = User("secret123")
print(u.verify("secret123"))
📝 Practice Exercises
Exercise 1
Create a class Laptop with a private attribute __price and methods to get and update the price.
Exercise 2
Create a class Student with a protected attribute _grade and print it through an object.
Exercise 3
Write a class with private data and demonstrate name mangling.
Exercise 4
Create a class Account that restricts setting a negative balance using a setter method.
✅ Practice Answers
Answer 1
class Laptop:
def __init__(self, price):
self.__price = price
def get_price(self):
return self.__price
def set_price(self, value):
if value > 0:
self.__price = value
l = Laptop(1200)
print(l.get_price())
Answer 2
class Student:
def __init__(self, grade):
self._grade = grade
s = Student("A")
print(s._grade)
Answer 3
class Demo:
def __init__(self):
self.__value = 50
d = Demo()
print(d._Demo__value)
Answer 4
class Account:
def __init__(self, balance):
self.__balance = balance
def set_balance(self, amt):
if amt >= 0:
self.__balance = amt
def get_balance(self):
return self.__balance
acc = Account(500)
acc.set_balance(300)
print(acc.get_balance())