Encapsulation is one of the four main pillars of Object-Oriented
Programming (OOP) (along with inheritance, polymorphism, and
abstraction).
It means binding data (variables) and methods (functions) that
operate on that data within a single unit (class) and restricting
direct access to some of the object’s internal data.
In simple terms:
Encapsulation = Data Hiding + Controlled Access
💡 Purpose of Encapsulation
Protect sensitive data from unauthorized access.
Control how data is modified (via getter and setter methods).
Keep the internal representation of an object hidden from the
outside world.
Maintain code integrity and security.
🔹 Example 1: Basic Encapsulation
class Student:
def __init__(self, name, marks):
[Link] = name # public variable
self.__marks = marks # private variable (encapsulated)
def show(self):
print(f"Name: {[Link]}, Marks: {self.__marks}")
# getter method
def get_marks(self):
return self.__marks
# setter method
def set_marks(self, marks):
if 0 <= marks <= 100:
self.__marks = marks
else:
print("Invalid marks!")
obj = Student("Abhay", 85)
[Link]()
obj.set_marks(92) # controlled access through setter
print(obj.get_marks()) # controlled access through getter
📘 Output:
Name: Abhay, Marks: 85
92
🔍 What’s Happening Internally
__marks is a private variable (name-mangled to _Student__marks).
You can’t access it directly outside the class.
You must use methods (get_marks, set_marks) to read or modify it
safely.
If you try:
print(obj.__marks)
❌ You’ll get:
AttributeError: 'Student' object has no attribute '__marks'
✅ But internally you could still access:
print(obj._Student__marks)
(not recommended — breaks encapsulation)
🔹 Example 2: Encapsulation Using Getters and Setters (Pythonic
Way)
Python provides a built-in decorator @property to create getters and
setters easily.
class BankAccount:
def __init__(self, balance):
self.__balance = balance
@property
def balance(self): # getter
return self.__balance
@[Link]
def balance(self, amount): # setter
if amount >= 0:
self.__balance = amount
else:
print("Invalid balance!")
account = BankAccount(1000)
print([Link]) # uses getter
[Link] = 5000 # uses setter
print([Link])
📘 Output:
1000
5000
🔐 Access Modifiers in Python (for Encapsulation)
Python doesn’t have true access modifiers like private, protected, or
public (as in Java or C++),
but it follows naming conventions to represent them:
Modifie Synt
Access Description
r ax
Public var Anywhere Default — accessible from anywhere
Protecte Class &
_var Indicated by a single underscore
d Subclass
Only inside Name mangled to prevent accidental
Private __var
class access
🔹 Example 3: Demonstrating Access Levels
class Employee:
def __init__(self):
[Link] = "Public" # Public variable
self._dept = "Protected" # Protected variable
self.__salary = 50000 # Private variable
obj = Employee()
print([Link]) # ✅ Accessible
print(obj._dept) # ⚠️Accessible (but should be used in subclass only)
# print(obj.__salary) # ❌ Error (private)
print(obj._Employee__salary) # ✅ Works, but not recommended
📘 Output:
Public
Protected
50000
🧩 Why Encapsulation Matters
Benefit Explanation
✅ Data Protection Sensitive data (like passwords or balance) can’t be
Benefit Explanation
modified directly.
✅ Code
Changes in internal logic won’t affect outside code.
Maintainability
✅ Flexibility Controlled access via getter/setter allows validation.
✅ Reusability Encapsulated classes are easier to reuse and test.
🧠 Encapsulation vs Abstraction
Concept Encapsulation Abstraction
Binding data and methods
Hiding implementation details
Meaning together; hiding internal
and showing only functionality.
data.
How data is stored and How data is used and
Focus
protected. presented.
Implementati Through access modifiers Through abstract classes and
on and getters/setters. interfaces.
🔹 Example 4: Real-Life Analogy
Think of a capsule medicine 💊
The outer shell = the class (encapsulation)
The medicine inside = data (private variables)
You can’t touch the medicine directly — only take it through
proper method (getter/setter).
💬 In Summary
Key Concept Description
Encapsulati Binding of data and functions that operate on data
on in one unit.
Purpose Data protection and controlled access.
Key Concept Description
Access
Achieved using _ and __ prefixes.
Control
Tools Getters, Setters, and @property.
Foundation of OOP and a key to secure,
Relation
maintainable code.
Would you like me to generate a PDF (with diagrams showing how
encapsulation works and access levels comparison)?
You said:
getter and setter
ChatGPT said:
🧠 Getter and Setter in Python (Detailed Explanation)
⚙️Definition
In Object-Oriented Programming (OOP),
getters and setters are special methods used to access (get) and
modify (set) the value of private (encapsulated) variables safely.
They provide controlled access to class attributes — preventing direct
modification and maintaining data integrity.
🧩 Why We Need Getter and Setter
In Python, all class attributes are public by default, meaning they can be
accessed or modified directly:
class Student:
def __init__(self, name, marks):
[Link] = name
[Link] = marks
obj = Student("Abhay", 95)
print([Link]) # Accessible directly
[Link] = 9999 # Can modify directly
🛑 This breaks encapsulation — anyone can change internal data freely.
✅ Solution → Use getter and setter methods for controlled access.
🔹 Example 1: Manual Getter and Setter
class Student:
def __init__(self, name, marks):
self.__name = name
self.__marks = marks # private variable
# getter method
def get_marks(self):
return self.__marks
# setter method
def set_marks(self, marks):
if 0 <= marks <= 100:
self.__marks = marks
else:
print("Invalid Marks! Must be between 0 and 100.")
obj = Student("Abhay", 85)
print(obj.get_marks()) # ✅ use getter
obj.set_marks(92) # ✅ use setter
print(obj.get_marks())
obj.set_marks(200) # ❌ invalid value
📘 Output:
85
92
Invalid Marks! Must be between 0 and 100.
🔍 Explanation
__marks is private (encapsulated).
get_marks() safely retrieves the value.
set_marks() safely updates it with validation.
If someone tries:
obj.__marks = 999
Python will not modify the real attribute (it creates a new one).
The real data remains protected inside _Student__marks.
🔹 Example 2: Pythonic Way (Using @property Decorators)
Python offers a cleaner way to define getters and setters using the
@property decorator.
class Student:
def __init__(self, name, marks):
self.__name = name
self.__marks = marks
@property
def marks(self): # getter
return self.__marks
@[Link]
def marks(self, value): # setter
if 0 <= value <= 100:
self.__marks = value
else:
print("Invalid Marks! Must be between 0 and 100.")
obj = Student("Abhay", 90)
print([Link]) # getter called automatically
[Link] = 95 # setter called automatically
print([Link])
[Link] = 150 # invalid value
📘 Output:
90
95
Invalid Marks! Must be between 0 and 100.
✅ Advantages of @property Approach
Without @property With @property
obj.get_marks() [Link]
obj.set_marks(95) [Link] = 95
Verbose and less
Cleaner and Pythonic syntax
readable
Native to Python’s design
Similar to Java style
philosophy
🔹 Example 3: Getter Only (Read-Only Property)
If you only define a getter, the variable becomes read-only:
class BankAccount:
def __init__(self, balance):
self.__balance = balance
@property
def balance(self): # only getter, no setter
return self.__balance
acc = BankAccount(1000)
print([Link]) # ✅ works
# [Link] = 2000 # ❌ AttributeError (no setter)
📘 Output:
1000
🔹 Example 4: Setter with Validation
class Employee:
def __init__(self, salary):
self.__salary = salary
@property
def salary(self):
return self.__salary
@[Link]
def salary(self, value):
if value > 0:
self.__salary = value
else:
print("Salary must be positive!")
emp = Employee(30000)
[Link] = 35000 # valid
print([Link])
[Link] = -1000 # invalid
📘 Output:
35000
Salary must be positive!
🔐 Summary Table
Concept Description Example
Method that returns the value of a
Getter @property
private variable
Method that modifies a private variable
Setter @[Link]
safely
Converts a method into a property for
@property [Link]
attribute-style access
Encapsulati Binding of data and methods with Using getters and
on restricted access setters
🧠 Key Takeaways
Getters and setters protect data from direct modification.
@property is the Pythonic way to use them.
They help enforce validation and data integrity.