In Python, Encapsulation is a fundamental concept of object-oriented programming (OOP). It refers to class as collections of data (variables) and methods that operate on that data.
Encapsulation restricts some components of an object from being accessed directly, so that unintended interference and data corruption may be prevented.
How does Encapsulation Work in Python?
Encapsulation is implemented in Python by stopping users from directly accessing certain parts of an object, while giving them the ability to access those areas through other means (methods).
Access can be controlled using different access modifiers:
- Public Attribute: Accessible from anywhere.
- Protected Attributes (_singleUnderscore): Not intended for public use, but still accessible.
- Private Attributes (__doubleUnderscore): Not directly accessible from outside the class.
| Member Type | Syntax | Accessible Inside Class | Accessible in Subclasses | Accessible Outside Class |
|---|---|---|---|---|
| Public | self.var | Yes | Yes | Yes |
| Protected | self._var | Yes | Yes (Recommended inside subclasses only) | Yes (Not recommended) |
| Private | self.__var | Yes | No (Unless using name mangling) | No (Direct access restricted) |
Implementation of Encapsulation in Python
Python uses three levels of access control for class members:
- public,
- protected, and
- private.
Let’s explore each with examples.
Public Members
Public members can be accessed everywhere, inside the class, outside the class, and inside derived (child) classes.
- Usage: No underscore before the variable name.
Python Public Members Example
Let us take an example to demonstrate public members in Python.
Example
class Car:
def __init__(self, brand, model):
self.brand = brand # Public attribute
self.model = model # Public attribute
def display(self):
print(f"Car: {self.brand} {self.model}")
# Creating an object
car = Car("Toyota", "Corolla")
# Accessing public members
print(car.brand)
print(car.model)
# Calling public method
car.display()
Output:
Toyota
Corolla
Car: Toyota Corolla
Explanation:
Public attributes (brand, model) will also be accessible outside the class. The display() method which is also public can be accessed from other classes.
Protected Members
Protected members are indicated by a single underscore (_variable).
- Usage: It can be accessed outside the class but should only be accessed within the class and subclasses (not enforced, just a convention).
Python Protected Members Example
Let us take an example to demonstrate protected members in Python.
Example
class Car:
def __init__(self, brand, model, engine):
self.brand = brand # Public attribute
self._model = model # Protected attribute
self._engine = engine # Protected attribute
def _show_details(self): # Protected method
print(f"Brand: {self.brand}, Model: {self._model}, Engine: {self._engine}")
class ElectricCar(Car):
def __init__(self, brand, model, battery_capacity):
super().__init__(brand, model, "Electric")
self.battery_capacity = battery_capacity
def show_info(self):
self._show_details() # Accessing protected method from subclass
print(f"Battery: {self.battery_capacity} kWh")
# Creating an object of ElectricCar
tesla = ElectricCar("Tesla", "Model S", 100)
# Accessing protected members from subclass
tesla.show_info()
# Accessing protected members outside the class (not recommended)
print(tesla._model) # Works, but not recommended
Output:
Brand: Tesla, Model: Model S, Engine: Electric
Battery: 100 kWh
Model S
Explanation:
_model and _engine are protected attributes,_show_details() is a protected method. They can be accessed in subclasses, but it’s not recommended to use them directly outside the class.
Private Members
Private members are indicated by double underscores (__variable).
- Usage: They cannot be accessed directly outside the class.
Python Private Members Example
Let us take an example to demonstrate private members in Python.
Example
class BankAccount:
def __init__(self, account_number, balance):
self.account_number = account_number # Public attribute
self.__balance = balance # Private attribute
def get_balance(self): # Getter method
return self.__balance
def set_balance(self, amount): # Setter method
if amount >= 0:
self.__balance = amount
else:
print("Invalid amount! Balance cannot be negative.")
# Creating an account object
account = BankAccount("123456789", 1000)
# Accessing public member
print(account.account_number) # Works fine
# Trying to access private member directly (will raise AttributeError)
# print(account.__balance) # Uncommenting this will cause an error
# Using getter method to access private attribute
print(account.get_balance()) # Works fine
# Using setter method to update private attribute
account.set_balance(2000)
print(account.get_balance()) # Updated balance
# Accessing private attribute using name mangling (Not recommended)
print(account._BankAccount__balance) # Works, but should be avoided
Output:
123456789
1000
2000
2000
Explanation:
__balance is a private attribute; direct access is not allowed. We use getter (get_balance()) and setter (set_balance()) methods to control access. Python renames __balance internally as _BankAccount__balance, allowing access via name mangling (but this is bad practice).
Conclusion
Encapsulation hides the internal details and the implementation of the object’s attributes by preventing direct access. In Python, encapsulation is applied through public, protected, and private members for class attributes, and controlling access through getters and setters. It improves the security, maintainability and structure of the code because of the convention-based approach in Python.
Leave a Reply