Data Hiding in Python
Data hiding is a fundamental concept in object-oriented programming in Python. It is a mechanism to hide the data of a class from the other classes or outside the world.
We do this to protect the data of an object from direct modification or any accidental access, which is not considered safe.
In Python, we can achieve or implement data hiding using a double underscore (__) before the data attribute (i.e. variable) we want to hide. For example:
# Define a class MyClass
class MyClass:
# Constructor method to initialize an instance attribute.
def __init__(self):
# Initialize a private variable with the value 42
self.__my_private_var = 42
# Public instance method.
def my_method(self):
# Print the value of the private variable
print(self.__my_private_var)
This example code defines a class called MyClass with a private variable __my_private_var and a public method my_method that prints the value of that private variable.
Basic Example of Data Hiding in Python
Let’s take a very basic example program of data hiding in Python.
Example 1:
# Python program to implement data hiding.
# Define a class named BankAccount.
class BankAccount:
# Define a constructor method to initialize the instance data attributes.
def __init__(self, accountNumber, balance):
self.accountNumber = accountNumber
# Encapsulated data attribute (private, accessible through methods)
self.__balance = balance # Data hiding
# Define a public deposit() method to add amount to the account balance.
def deposit(self, amount):
if amount > 0:
self.__balance += amount # Increment the private balance attribute
# Define a public 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!")
# Define a public getter method to access the private balance attribute.
def get_balance(self):
return self.__balance
In this example, we have defined a class named BankAccount that encapsulates the accountNumber and balance as data attribute and deposit(), withdraw(), and get_balance() as methods that work on these data.
Inside the BankAccount class, we have hidden the attribute balance from outside the methods because it is private declared. We cannot directly access the private attribute, like this:
account = BankAccount("345675", 1500)
print(account.__balance) # AttributeError: 'BankAccount' object has no attribute '__balance'.
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("345675", 1500)
# Accessing encapsulated attribute and methods
# Deposit 600 into the account using the deposit method.
account.deposit(600)
# Withdraw 300 from the account using the withdraw method.
account.withdraw(300)
# Access and print the account balance using the get_balance method.
print("Account balance: ", account.get_balance())
Output:
Account balance: 1800
Example 2:
# Python program to implement data hiding.
# Define a User class to store sensitive user data
class User:
def __init__(self, username, password):
# Initialize private variables to store username and password
self.__username = username
self.__password = password
def authenticate(self, input_username, input_password):
# Method to authenticate a user by comparing input username and password
if input_username == self.__username and input_password == self.__password:
return True
else:
return False
# Create an instance of class User by passing a username and password to its constructor method.
user1 = User("my_username", "my_secure_password")
# Attempt to authenticate with correct username and password
if user1.authenticate("my_username", "my_secure_password"):
print("Authentication is successful.")
else:
print("Authentication is failed.")
# Attempt to authenticate with incorrect username and password
if user1.authenticate("wrong_username", "wrong_password"):
print("Authentication is successful.")
else:
print("Authentication is failed.")
Output:
Authentication is successful.
Authentication is failed.
In this example code, we have defined a class named User that contains private variables to store username and password. The authenticate method allows us to check if a given username and password match the stored credentials. The above sample code demonstrates successful and failed authentication attempts.
Encapsulation and Data Hiding in Action
Let’s take an example program to illustrate how encapsulation and data hiding does work in the real-world situation. Suppose we are developing a program for student information system used in a college to manage student records, grades, and personal information.
We will create a class student, which encapsulates the data attributes of a student and methods defined to interact with these details.
Example 3:
class Student:
def __init__(self, name, age):
self.__name = name # Private variable
self.__age = age # Private variable
def get_name(self):
return self.__name
def get_age(self):
return self.__age
def set_age(self, age):
if age > 0:
self.__age = age
# Create a Student object
student = Student("Alice", 20)
# Access private attributes using getter methods
print("Student Name:", student.get_name())
print("Student Age:", student.get_age())
# Attempt to access private attributes directly (data hiding)
# This will result in an AttributeError
# print(student.__name) # This line will raise an error
# Modify the age using a setter method
student.set_age(21)
# Display the updated information
print("Updated Student Age:", student.get_age())
Output:
Student Name: Alice
Student Age: 20
Updated Student Age: 21
In this simple example, we have defined a class Student that encapsulates the private attributes such as name and age. This class contains getter and setter methods, such as get_name(), get_age(), and set_age() that interact with the encapsulated data. Note that __name and __age attributes are hidden, so it cannot be accessed and modified directly.
If we try to access or modify them, they will generate an error. Instead, we can only interact with it through methods defined inside the Student class. This simple example shows that how encapsulation and data hiding work together by preventing direct access to private attributes and controlling access through methods.
Overall, encapsulation and data hiding are powerful concepts in OOP that help us to develop safe and maintainable software applications. By correctly implementing in the program, we can ensure that each part of the program code does its job independently, reducing the possibility of errors and making our program code easier to read, write, and manage.
Advanced Example Program based on Data Hiding and Encapsulation
Let’s take one more advanced example program in which we will demonstrate how encapsulation and data hiding work in Python. Look at the program code below:
Example 4:
class Person:
def __init__(self, name, age):
self.__name = name # Private variable
self.__age = age # Private variable
def get_name(self):
return self.__name
def get_age(self):
return self.__age
def __str__(self):
return f"Name: {self.__name}, Age: {self.__age}"
class BankAccount:
def __init__(self, account_number, balance, owner):
self.__account_number = account_number # Private variable
self.__balance = balance # Private variable
self.__owner = owner # Private variable
def deposit(self, amount):
if amount > 0:
self.__balance += amount
def withdraw(self, amount):
if amount > 0 and amount <= self.__balance:
self.__balance -= amount
def get_balance(self):
return self.__balance
def __str__(self):
return f"Account Number: {self.__account_number}, Balance: {self.__balance}"
class Bank:
def __init__(self):
self.accounts = [] # empty list.
def add_account(self, account):
self.accounts.append(account)
def list_accounts(self):
for account in self.accounts:
print(account)
# Create an instance of Person class.
person = Person("Alice", 30)
# Create instances of BankAccount class by passing arguments such as account number, balance, and owner.
account1 = BankAccount("12345", 1000, person)
account2 = BankAccount("67890", 500, person)
# Create a bank object and add the accounts.
bank = Bank()
bank.add_account(account1)
bank.add_account(account2)
# Accessing private attributes through getter methods.
print(f"Person: {person.get_name()}, Age: {person.get_age()}")
print("\nBank Accounts:")
bank.list_accounts()
# Perform operations on the accounts
account1.deposit(500)
account2.withdraw(200)
# Display the updated bank account information.
print("\nUpdated Bank Accounts:")
bank.list_accounts()
Output:
Person: Alice, Age: 30
Bank Accounts:
Account Number: 12345, Balance: 1000
Account Number: 67890, Balance: 500
Updated Bank Accounts:
Account Number: 12345, Balance: 1500
Account Number: 67890, Balance: 300
In this example, we have first created a class named Person that contains private attributes (__name and __age). This class also contains getter methods (get_name() and get_age()) to access private attributes. Inside this class, we have also defined __str__() method that provides a string representation of the object.
Then, we have defined a class BankAccount that contains private attributes __account_number, __balance, and __owner. This class also contains deposit() and withdraw() methods for depositing and withdrawing money. We have defined a getter method get_balance() to access the balance attribute. The __str__() method provides a string representation of the object.
After that, we have defined a class named Bank that manages multiple bank accounts. It contains an empty list of accounts (self.accounts) and methods for adding an account (add_account()) in the list and listing all accounts (list_accounts()).
Outside the class definition, we have created an instance of the Person class by passing argument values such as “Alice” and 30. Here, Alice represents the name of person and 30 is his age.
Then, we have created two instances of the BankAccount class by passing arguments such as account number, balance, and owner. After that, we have created an instance of class Bank and added these accounts by passing them to add_account() method.
We have used getter methods to access the private attributes and indirect methods to access and manipulate bank accounts. At last, the program displays the information about the person and bank accounts, performs deposit and withdrawal operations, and displays the updated account information on the console.
In this tutorial, we have discussed data hiding in Python with the help of various examples. Hope that you will have understood the basic concept of data hiding and practiced all programs. Stay tuned with the next tutorial where you will learn ways to implement method overloading in Python with the help of advanced example programs.
Thanks for reading!!!



