Encapsulation is an important concept of Object-Oriented Programming (OOP). It means bundling data and the methods that operate on that data inside a class.
Encapsulation also helps control how the data of an object can be accessed or modified. In Python, encapsulation is commonly implemented using public, protected, and private naming conventions.
Encapsulation means keeping data and the methods that work with that data together inside a class.
It also provides a way to control direct access to the internal data of an object.
class Student:
def __init__(self, name, marks):
self.name = name
self.marks = marks
def display(self):
print(self.name)
print(self.marks)
s1 = Student("Rahul", 85)
s1.display()
Rahul 85
Encapsulation provides several benefits:
Python mainly uses three naming conventions for data members:
_.__.A public member can be accessed directly from outside the class.
class Student:
def __init__(self):
self.name = "Rahul"
student = Student()
print(student.name)
Rahul
Here, name is a public attribute.
class Account:
def __init__(self):
self.balance = 5000
account = Account()
print(account.balance)
account.balance = 7000
print(account.balance)
5000 7000
Because balance is public, it can be accessed and modified directly.
A protected member is normally written with a single underscore before its name.
class Student:
def __init__(self):
self._marks = 85
student = Student()
print(student._marks)
85
The single underscore is mainly a convention that tells programmers that the member is intended for internal or subclass use.
class Parent:
def __init__(self):
self._value = 100
class Child(Parent):
def show(self):
print(self._value)
obj = Child()
obj.show()
100
A subclass can normally access a protected-style attribute.
A private member is written using two underscores before its name.
class Student:
def __init__(self):
self.__marks = 90
student = Student()
print(student.__marks)
The above code produces an error because __marks is name-mangled.
When an attribute starts with two underscores, Python performs name mangling.
class Student:
def __init__(self):
self.__marks = 90
student = Student()
print(student._Student__marks)
90
Python internally changes __marks to approximately _Student__marks.
Private attributes are useful when you want to prevent normal direct access to internal implementation details.
class BankAccount:
def __init__(self, balance):
self.__balance = balance
def show_balance(self):
print(self.__balance)
account = BankAccount(10000)
account.show_balance()
10000
Instead of allowing direct modification of private data, we can provide methods to control access.
class BankAccount:
def __init__(self, balance):
self.__balance = balance
def deposit(self, amount):
self.__balance += amount
def show_balance(self):
print(self.__balance)
account = BankAccount(5000)
account.deposit(2000)
account.show_balance()
7000
A getter method is used to retrieve the value of an internal attribute.
class Student:
def __init__(self):
self.__marks = 85
def get_marks(self):
return self.__marks
student = Student()
print(student.get_marks())
85
A setter method can be used to change the value of an internal attribute.
class Student:
def __init__(self):
self.__marks = 0
def set_marks(self, marks):
self.__marks = marks
def get_marks(self):
return self.__marks
student = Student()
student.set_marks(90)
print(student.get_marks())
90
Encapsulation allows us to validate data before storing it.
class Student:
def __init__(self):
self.__marks = 0
def set_marks(self, marks):
if 0 <= marks <= 100:
self.__marks = marks
else:
print("Invalid marks")
def get_marks(self):
return self.__marks
student = Student()
student.set_marks(85)
print(student.get_marks())
85
class BankAccount:
def __init__(self):
self.__balance = 0
def set_balance(self, amount):
if amount >= 0:
self.__balance = amount
else:
print("Balance cannot be negative")
def get_balance(self):
return self.__balance
account = BankAccount()
account.set_balance(-500)
print(account.get_balance())
Balance cannot be negative 0
Python provides the @property decorator to create controlled attribute access.
class Student:
def __init__(self, marks):
self.__marks = marks
@property
def marks(self):
return self.__marks
student = Student(90)
print(student.marks)
90
The @property decorator can be combined with @marks.setter to control assignment.
class Student:
def __init__(self, marks):
self.__marks = marks
@property
def marks(self):
return self.__marks
@marks.setter
def marks(self, value):
if 0 <= value <= 100:
self.__marks = value
else:
print("Invalid marks")
student = Student(80)
student.marks = 95
print(student.marks)
95
Consider a bank account. The account balance should not normally be changed directly. Instead, operations such as deposit and withdrawal can control how the balance changes.
class BankAccount:
def __init__(self, balance):
self.__balance = balance
def deposit(self, amount):
if amount > 0:
self.__balance += amount
def withdraw(self, amount):
if 0 < amount <= self.__balance:
self.__balance -= amount
else:
print("Invalid withdrawal")
def get_balance(self):
return self.__balance
account = BankAccount(10000)
account.deposit(2000)
account.withdraw(3000)
print(account.get_balance())
9000
Encapsulation helps protect the internal state of an object by controlling how data is accessed or changed.
class Employee:
def __init__(self, salary):
self.__salary = salary
def get_salary(self):
return self.__salary
def set_salary(self, salary):
if salary > 0:
self.__salary = salary
employee = Employee(30000)
employee.set_salary(35000)
print(employee.get_salary())
35000
| Type | Syntax | Meaning |
|---|---|---|
| Public | name |
Normally accessible from anywhere |
| Protected | _name |
Intended for internal or subclass use |
| Private | __name |
Name-mangled to discourage direct access |
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("Invalid salary")
employee = Employee("Amit", 40000)
print(employee.name)
print(employee.get_salary())
employee.set_salary(45000)
print(employee.get_salary())
Amit 40000 45000
@property decorator provides convenient controlled attribute access.Question: Which naming style is commonly used for a private attribute in Python?