Encapsulation in Python
The feature of wrapping up data (attributes) and methods associated with that data into a single unit is called encapsulation in Python. In any other programming languages or Python, this unit is a class.
For example, scooter parts like engine, brakes, wheels, and methods like start(), stop(), accelerate() encapsulate together to form a scooter.
Similarly, a class is an example of encapsulation that combines data and methods together into a single unit and hides their internal details from outside the world.
Another real-time example of encapsulation is a capsule. Essentially, a capsule encapsulates several combinations of medicine. If combinations of medicine represent variables and methods, then the capsule will act as a class and the entire process refers to encapsulation as shown in the below figure.
Encapsulation is one of the four fundamental principles of OOP, along with inheritance, polymorphism, and abstraction. Data members of a class in encapsulation are only accessible through its member functions. It preserves the data safe from misuse and any external interference.
Thus, we can say that encapsulation is a programming technique that involves bundling the attributes (variables) and methods (functions) of a class into a single unit (the class itself) and controlling their access through the use of access modifiers and member functions only.
Why do We Need Encapsulation in Python?
Let’s understand the need for or use of encapsulation in Python with the help of real-time examples.
1. Realtime Example – Bank Account
Imagine you have a bank account in a bank. Each bank account has a balance, which is a crucial piece of data. If the balance variable is declared as a public in the banking application, the balance variable becomes publicly accessible. In such a case, anyone can easily see your account balance, which is certainly not desirable.
To protect it from unauthorized access or modification, the balance variable is declared as private in the banking application so that your account balance is not directly accessible to anyone. It is controlled through methods defined within the class. To access the account balance, a user must provide authentication, such as the account holder name or user id and password.
By encapsulating related data and methods, we maintain the integrity of data and prevents unauthorized access. We achieve a higher degree of security and privacy. This is the best real-time example to understand encapsulation feature that promotes data protection and controlled access.
2. Realtime Example – Gmail Account
When you log in your Gmail account, numerous internal processes occur in the background, and you, as a user, have no direct control over them. The username and password associated with a Gmail account are sensitive pieces of data that need protection.
By encapsulating these as private attributes within a class, we prevent unauthorized access to or modification of these credentials. This safeguards the user’s account from unauthorized access.
By abstracting away the internal working details and providing a controlled interface for authentication, encapsulation keeps your account safe from misuse.
Encapsulation is a fundamental principle in object-oriented programming that plays a vital role in maintaining the security and integrity of sensitive data in various applications, including email services.
How to Achieve Encapsulation in Python
In Python, we can achieve encapsulation through the use of access modifiers. These modifiers determine the visibility and accessibility of class members (variables and methods) from outside the class or world. There are three types of access modifiers in Python programming. They are as:
(1) Public: Public members of a class can be accessible from anywhere, both within the class and outside the class. By default, all the members (attributes and methods) of a class are public and can be viewed by any other class. For example:
class Person:
def __init__(self):
self.name = name # public member
def greet(self):
print("Hello, my name is ", self.name)
In the above example code, we have defined a class named Person with a public attribute called “name” and a public method named “greet”. We can access these attributes from outside the class as well as this:
# Creating an object of class Person.
person = Person("Mahi")
# Accessing public attribute.
print(person.name) # prints "Mahi"
# Accessing public method.
person.greet() # prints "Hello, my name is Mahi"
(2) Protected: Protected members of a class are accessible within the class and its subclasses. We denote the protected members by a single underscore (_) prefix. For example:
class BankAccount:
def __init__(self):
# Protected attribute or instance variable.
self._balance = balance
# Protected method.
def _withdraw(self):
self._balance -= amount
In this example, we have defined a class name “BankAccount” with a protected attribute or instance variable named “_balance” and a protected method named “_withdraw”. We can access these protected members of a class from within the class and its subclasses.
(3) Private: Private members of a class are only accessible within the class itself. We denote them by a double underscore (__). If we attempt to access private members of a class from outside the class, we will get an AttributeError. For example:
class Employee:
def __init__(self, name, salary):
# Private attributes or instance variables.
self.__name = name
self.__salary = salary
# Private method.
def __display_info(self):
print("Name: ", self.__name)
print("Salary: ", self.__salary)
In this example code, we have defined a class named “Employee” with private attributes named “__name” and “__salary” and a private method named “__display_info”. These attributes and method can only be accessible within the class. If we try to access these members from outside the class, we will get an AttributeError because they are private members.
Implementing Encapsulation in Python
Let’s take a simple example program in which we will implement encapsulation feature in Python. To implement encapsulation, we will use two access modifiers (public and private) to control the visibility and accessibility of class members.
Example 1:
# Python program to demonstrate the encapsulation feature.
# Define a class named BankAccount.
class BankAccount:
# Constructor method to initialize the instance attributes.
def __init__(self, acc_number, balance):
# Public attribute (accessible from outside)
self.acc_number = acc_number
# Encapsulated attribute (private, accessible through methods)
self.__balance = balance
# Deposit method to add amount to the account balance.
def deposit(self, amount):
if amount > 0:
self.__balance += amount # Increment the private balance attribute
# Withdraw method to deduct amount from the account balance.
def withdraw(self, amount):
if amount > 0 and amount <= self.__balance:
self.__balance -= amount # Decrement the private balance attribute
else:
# Display an error message if the balance is insufficient.
print("Insufficient balance in your account!")
# Getter method to access the private balance attribute.
def get_balance(self):
return self.__balance
As we know that we encapsulate data and methods by creating a class in Python. In this example, we have created a class BankAccount that encapsulates the acc_number and balance as data attribute and deposit(), withdraw(), and get_balance() as methods that work on these data. Inside the above BankAccount class, we have hidden the attribute balance from outside the methods because it is private declared.
In the encapsulation technique, when we declare data attribute (i.e. variables) as private in the class to prevent accessing them directly, it is called data hiding in Python. With data hiding, we can hide data attributes within the class. It restricts direct access to data attributes from outside the class like this:
account = BankAccount("12345", 1000)
print(account.__balance) # AttributeError: 'BankAccount' object has no attribute '__balance'.
Accessing Encapsulated Data Attributes
In this case, we can access the required encapsulated data by using public getter and setter methods in Python. In the above BankAccount class, we have defined a getter method named get_balance that allows us to access the private __balance attribute.
The deposit() and withdraw() methods acts as setter methods that allow us to update or change the value of __balance attribute. Look at the below code with comments.
# Create a bank account object with an account number and initial balance.
account = BankAccount("12345", 1000)
# Accessing encapsulated attribute and methods
# Deposit 500 into the account using the deposit method.
account.deposit(500)
# Withdraw 200 from the account using the withdraw method.
account.withdraw(200)
# Access and print the account balance using the get_balance method.
print("Account balance: ", account.get_balance())
Output:
Account balance: 1300
Let’s take one more example program based on the encapsulation and data hiding in Python for the best practicing.
Example 2:
# Define a class called 'Student'.
class Student:
# Constructor method to initialize the instance attributes.
def __init__(self, name, age):
# Initialize the '_name' and '__age' attributes with the provided 'name' and 'age'.
# Encapsulated members.
self.__name = name # private member.
self.__age = age # private member.
# Getter method for retrieving the student's name.
def get_name(self):
return self.__name
# Setter method for updating the student's name.
def set_name(self, name):
# Update the private variable '__name' with a new value.
self.__name = name
# Getter method for retrieving the student's age.
def get_age(self):
return self.__age
# Setter method for updating the student's age with a validation check.
def set_age(self, age):
# Check if the provided 'age' is greater than 0.
if age > 0:
# Update the private variable '__age' with a new value.
self.__age = age
In this example, we have defined a class Student that contains two encapsulated members: __name and __age. Both members of the class are private. To access these private members from outside the class definition, it is necessary to create public methods that allow to access these attributes.
That’s why, we have defined public getter and setter methods to access and modify / update these private attributes. Inside the class, we have also defined a constructor method to initialize the instance variables __name and __age with values.
Now, let’s see how can we access and modify the encapsulated data attributes in the above example:
# Create an instance of the 'Student' class by passing argument values.
student = Student("John", 20)
# Access the student's name using get_name() method and print it.
print("Student's Name: ", student.get_name())
# Access the student's age using get_age() method and print it.
print("Student's Age: ", student.get_age())
# Call set_name() method to update the student's name.
student.set_name("Bob")
# Access the updated name using get_name() method and print it.
print("Updated Name: ", student.get_name())
# Call set_age() method to change the student's age with validation.
student.set_age(22)
# Access the updated age using get_age() method and print it.
print("Updated Age: ", student.get_age())
Output:
Student's Name: John
Student's Age: 20
Updated Name: Bob
Updated Age: 22
As you can see in the above code, we have accessed and updated private members by using getter and setter methods, ensuring that we followed the encapsulation principle. Thus, we can control the visibility and accessibility of private class members through getter and setter methods.
Encapsulation without Getter and Setter Method
We can also achieve encapsulation without getter and setter methods in Python. In Python, we can directly access class attributes, but through getter and setter methods, to access and modify them is considered a best practice for encapsulation. Look at the below a simple example demonstrating encapsulation without getter and setter methods:
Example 2:
class Student:
def __init__(self, name, age):
# Encapsulated attributes
self._name = name # protected member.
self._age = age # protected member.
# Outside the class definition.
# Create a student object.
student = Student("Mahi", 20)
# Accessing encapsulated attributes directly.
print("Name: ", student._name)
print("Age: ", student._age)
# Modifying encapsulated attributes directly
student._name = "Mahikaa"
student._age = 22
# Accessing modified attributes
print("Modified Name: ", student._name)
print("Modified Age: ", student._age)
Output:
Name: Mahi
Age: 20
Modified Name: Mahikaa
Modified Age: 22
As you can observe in this code, we have used protected access modifier to encapsulate the data members of the class. Therefore, we have accessed and modified these attributes directly from outside the class definition.
However, we will use the private access modifier in the place of protected, we cannot access and modify without using getter and setter method. This is because private members are only accessible within the class. Look at the below example code.
Example 3:
class Student:
def __init__(self, name, age):
self.__name = name # Private attribute
self.__age = age # Private attribute
# Create a student object
student = Student("Alice", 20)
# Attempting to access private attributes directly will result in an error
# Uncomment the following lines to see the error messages
# print("Name: ", student.__name)
# print("Age: ", student.__age)
# Modifying private attributes directly will also result in an error
# Uncomment the following lines to see the error messages
# student.__name = "Bob"
# student.__age = 22
# Accessing private attributes using name mangling
print("Name: ", student._Student__name)
print("Age: ", student._Student__age)
# Modifying private attributes using name mangling
student._Student__name = "Bob"
student._Student__age = 22
# Accessing modified private attributes using name mangling
print("Modified Name: ", student._Student__name)
print("Modified Age: ", student._Student__age)
Output:
Name: Alice
Age: 20
Modified Name: Bob
Modified Age: 22
In this example code, the attributes __name and __age are private members because they are prefixed with a double underscore. Attempting to access or modify these attributes directly from outside the class will result in errors.
Instead, we can access them using name mangling, which involves prefixing the attribute name with _ and the class name (e.g., _Student__name). However, Python does not support encapsulation with the help of name mangling.
Best Practices for Encapsulation in Python
Following are some key points that you should keep in mind for the best practicing when implementing encapsulation feature in Python. They as:
(1) Use access modifiers (public, protected, and private) appropriately for each class member based on its intended visibility and accessibility. It clearly helps to define the extents of encapsulation.
(2) Instead of directly accessing members of the class, use public getter and setter methods to access and update them. This enables in achieving better encapsulation. It provides us to control how members are accessed and modified.
(3) Clearly comment on the public methods and their purpose to offer high level for other developers.
(4) Avoid the excessive use of protected modifier with members. However, protected members can be useful in certain cases, but it in general is recommended to treat them private and avoid accessing them directly from outside the class definition.
By following these key points, you can effectively implement encapsulation in your Python program and reap the benefits it provides.
Benefits of Encapsulation in Python
Encapsulation provides numerous benefits in the software development that are as follows:
(1) Data hiding: Encapsulation lets us to hide the internal implementation details of a class from outer the world and exposes only what is necessary. By declaring certain members as private or protected, we can restrict direct access and modification of data. These members can be accessed and updated only through controlled methods.
(2) Abstraction: Encapsulation helps to represent complex systems in a simplified manner. By revealing only the essential information and functionality with public methods, we can develop a high-level interface that hides the underlying complexity. This makes easier for other developers to use your code without worrying about the internal working details.
(3) Modularity: Encapsulation promotes modularity by encapsulating related data attributes and methods within a single unit called class. This makes it easier to comprehend and maintain the codebase because each class is responsible for a specific set of functionality. Changes made to a class are less likely to affect other parts of the code, leading to more modular and reusable code.
(4) Code Reusability: Encapsulation enables us to construct reusable constituents by encapsulating related data attributes and methods within a single class. We can easily reuse these constituents in different parts of the system or in other projects, saving development time and efforts.
Encapsulation is a fundamental principle of OOP that promotes data hiding, abstraction, modularity, and code reusability. We can achieve encapsulation in Python by using access modifiers and providing controlled access to class members.
Understanding and implementing encapsulation in your application program will lead to more maintainable and robust software systems. Hope that you will have understood the basic concept of encapsulation and practiced all example programs.
Thanks for reading!!!




