Introduction
Encapsulation is one of the fundamental concepts in object-oriented programming (OOP). It refers to the bundling of data (attributes) and methods (functions) that operate on the data into a single unit, or class. Encapsulation helps in restricting access to certain components of an object and can prevent the accidental modification of data.
Key Concepts
Public, Protected, and Private Members
- Public Members: Accessible from outside the class. They are defined without any leading underscores.
- Protected Members: Indicated by a single underscore (
_
). They are meant to be accessed within the class and its subclasses. - Private Members: Indicated by a double underscore (
__
). They are meant to be accessed only within the class.
Syntax
Defining a Class with Encapsulation
class MyClass:
def __init__(self, public, protected, private):
self.public = public # Public attribute
self._protected = protected # Protected attribute
self.__private = private # Private attribute
def get_private(self):
return self.__private
def set_private(self, value):
self.__private = value
Example
Let’s consider a simple example of a Car
class to demonstrate encapsulation.
Car Class
class Car:
def __init__(self, brand, model, price):
self.brand = brand # Public attribute
self._model = model # Protected attribute
self.__price = price # Private attribute
def get_price(self):
return self.__price
def set_price(self, price):
if price > 0:
self.__price = price
else:
print("Invalid price")
# Creating an object
my_car = Car("Toyota", "Corolla", 20000)
Explanation
- Public Attribute (
brand
): Accessible from outside the class directly. - Protected Attribute (
_model
): Intended to be accessed within the class and its subclasses. - Private Attribute (
__price
): Only accessible within the class using methods.
Using the Car Class
# Accessing public attribute
print(my_car.brand) # Output: Toyota
# Accessing protected attribute
print(my_car._model) # Output: Corolla
# Accessing private attribute using getter method
print(my_car.get_price()) # Output: 20000
# Modifying private attribute using setter method
my_car.set_price(25000)
print(my_car.get_price()) # Output: 25000
# Trying to access private attribute directly (will raise an AttributeError)
try:
print(my_car.__price)
except AttributeError as e:
print(e) # Output: 'Car' object has no attribute '__price'
Real-World Example: Bank Account
Let’s consider a real-world example of a bank account system.
BankAccount Class
class BankAccount:
def __init__(self, account_number, balance):
self.__account_number = account_number # Private attribute
self.__balance = balance # Private attribute
def get_balance(self):
return self.__balance
def deposit(self, amount):
if amount > 0:
self.__balance += amount
return f"Deposited {amount}. New balance is {self.__balance}."
else:
return "Invalid amount"
def withdraw(self, amount):
if 0 < amount <= self.__balance:
self.__balance -= amount
return f"Withdrew {amount}. New balance is {self.__balance}."
else:
return "Invalid amount or insufficient balance"
# Creating an object of the BankAccount class
account = BankAccount("1234567890", 10000)
Explanation
- Private Attributes (
__account_number
,__balance
): These are only accessible within the class using methods. - Public Methods (
get_balance
,deposit
,withdraw
): These methods provide controlled access to the private attributes.
Using the BankAccount Class
# Accessing and modifying balance using methods
print(account.get_balance()) # Output: 10000
print(account.deposit(5000)) # Output: Deposited 5000. New balance is 15000.
print(account.get_balance()) # Output: 15000
print(account.withdraw(3000)) # Output: Withdrew 3000. New balance is 12000.
print(account.get_balance()) # Output: 12000
# Trying to access private attribute directly (will raise an AttributeError)
try:
print(account.__balance)
except AttributeError as e:
print(e) # Output: 'BankAccount' object has no attribute '__balance'
Conclusion
Encapsulation in Python helps to protect the internal state of an object and prevents unauthorized access and modification. By using public, protected, and private attributes, you can control the access level of each attribute and method. This not only enhances the security of the code but also makes it more modular and maintainable. The real-world example of a bank account demonstrates how encapsulation can be applied to manage sensitive data effectively.