Introduction to Encapsulation¶
- Encapsulation is a fundamental concept in Object-Oriented Programming (OOP) that emphasizes data protection by grouping variables (attributes) and functions (methods) into a single unit — typically a class.
- This mechanism provides control over how data is accessed and modified, helping to safeguard the internal state of objects.
- Encapsulation helps in data hiding, security, and modularity.
Key Concepts of Encapsulation¶
Data Binding: Groups data and methods into one logical unit (class).
Data Hiding: Prevents unauthorized access to internal class variables.
Access Control: Provides controlled access to class members using public, protected, and private scopes.
Setters & Getters: Used to read or update private variables safely.
Prefixing Variables: In Python, encapsulation is implemented using underscore prefixes:
_var→ Protected__var→ Private
Why Encapsulation?¶
Prevents unwanted changes: External code can't freely modify internal variables.
Validation control: Rules can be added before accepting a value change.
Improved maintainability: Changes to internal structure don’t affect external code.
Code security: Protects object integrity by hiding sensitive information.
Access Modifiers in Python¶
- In Python, access modifiers control the visibility of class attributes and methods.
| Access Type | Syntax | Accessibility |
|---|---|---|
| Public | variable |
Accessible from anywhere |
| Protected | _variable |
Intended for use within class and subclasses |
| Private | __variable |
Name mangling hides from outside access |
Public Members Example¶
- Public members are accessible from anywhere, inside or outside the class.
- In Python, all attributes and methods are public by default.
# Example: Public data members are accessible from outside
class Developer:
def __init__(self, name, tech):
self.name = name # public attribute
self.tech = tech # public attribute
def show_info(self): # public method
print(f"{self.name} works with {self.tech}")
# Object creation
dev = Developer("Arjun", "Machine Learning")
# Accessing public members directly
print(dev.name, dev.tech) # Output: Arjun Machine Learning
dev.show_info() # Output: Arjun works with Machine Learning
Arjun Machine Learning Arjun works with Machine Learning
Protected Members Example¶
- Protected members (single underscore) are accessible within the class and its subclasses.
# Example: Protected members in parent and child classes
class Team:
def __init__(self):
self._project = "AI Chatbot" # protected member
class Engineer(Team):
def __init__(self, name):
super().__init__()
self.name = name
def display(self):
print(f"{self.name} is working on {self._project}")
eng = Engineer("Neha")
# Accessing through a method
eng.display() # Output: Neha is working on AI Chatbot
# Accessing protected attribute (works, but not recommended)
print(eng._project) # Output: AI Chatbot
Neha is working on AI Chatbot AI Chatbot
- Even though
_projectis marked as protected, Python does not enforce this rule. However, it is a convention that indicates it should not be accessed directly outside the class.
Private Members Example¶
- Private members are prefixed with a double underscore (__) and cannot be accessed directly from outside the class.
- This is useful for restricting access to sensitive data.
# Example: Attempting to access a private member
class BankAccount:
def __init__(self, name, balance):
self.name = name
self.__balance = balance # private member
# Object creation
account = BankAccount("Riya", 5000)
# Trying to access private member will raise an error
print(account.__balance) # Raises AttributeError
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) /tmp/ipython-input-1-1233685561.py in <cell line: 0>() 9 10 # Trying to access private member will raise an error ---> 11 print(account.__balance) # Raises AttributeError AttributeError: 'BankAccount' object has no attribute '__balance'
Accessing Private Members Safely¶
Using Getter and Setter Methods¶
These methods provide a controlled interface to access and update the values of private attributes.
Getter methods allow us to retrieve the value of a private variable.
Setter methods allow us to assign a new value while optionally applying validation or constraints.
# Example: Using getter and setter for private member
class Student:
def __init__(self, name, age):
self.name = name
self.__age = age # private member
def get_age(self):
return self.__age
def set_age(self, new_age):
if new_age > 0:
self.__age = new_age
else:
print("Invalid age")
stud = Student("Karan", 15)
print(stud.get_age()) # Output: 15
stud.set_age(18)
print(stud.get_age()) # Output: 18
15 18
Using Name Mangling¶
- In this approach, Python internally changes the name of a private variable by prefixing it with an underscore followed by the class name. The resulting format looks like:
_ClassName__variableName,
- where ClassName is the name of the class and variableName is the original private attribute.
# Accessing private variable with name mangling
class Vehicle:
def __init__(self):
self.__speed = 120
car = Vehicle()
print(car._Vehicle__speed) # Output: 120
120
Advantages of Encapsulation¶
| Feature | Benefit |
|---|---|
| Security | Restricts external access to sensitive data |
| Data Hiding | Internal implementation is hidden from outside classes |
| Validation | Enables adding logic before updating variables |
| Maintainability | Easier to update and debug code without affecting other modules |
| Reusability | Promotes modular, reusable class-based design |