1. Introduction
Encapsulation is the OOP principle of bundling data (attributes) and the methods that operate on that data into a single unit (a class), while restricting direct outside access to some of that internal state. Its goals are to protect an object's internal consistency and to hide implementation details behind a clean public interface.
Cricket analogy: Encapsulation is like a team's dressing room strategy being bundled with the players who execute it, kept away from opposition eyes—only the public scoreboard (interface) is visible, protecting the game plan's internal consistency.
Unlike languages such as Java or C++ that enforce access modifiers (private, protected, public) at the compiler level, Python relies on naming conventions and cooperative discipline among developers, backed by a lightweight technical mechanism called name mangling for double-underscore names.
Cricket analogy: Unlike a franchise with a strict contract clause barring player interviews (enforced), Python is like a team relying on a locker-room code of conduct—players (developers) are trusted not to leak internal strategy, backed by a mild deterrent like a fine (name mangling).
2. Syntax
class ClassName:
def __init__(self):
self.public_attr = 1 # accessible from anywhere
self._protected_attr = 2 # convention: internal use, subclasses ok
self.__private_attr = 3 # name-mangled to _ClassName__private_attr
@property
def private_attr(self): # controlled read access
return self.__private_attr
@private_attr.setter
def private_attr(self, value): # controlled write access with validation
if value < 0:
raise ValueError("must be non-negative")
self.__private_attr = value3. Explanation
Python uses three conventions for attribute visibility: a plain name (attr) is public and freely accessible; a single leading underscore (_attr) signals "internal use, please don't touch from outside," but is not enforced by the interpreter; a double leading underscore (__attr) triggers name mangling, where Python internally renames the attribute to _ClassName__attr, making accidental access or overriding from subclasses much less likely (though still possible if you know the mangled name).
Cricket analogy: A public attribute is like a player's jersey number, visible to all; a single-underscore attribute is like a "do not disturb" note on the dressing room door—respected but not locked; a double-underscore attribute is like a coded locker combination (_ClassName__attr) that's technically crackable but discourages casual access.
The @property decorator is the idiomatic way to expose controlled access to internal state: it lets you keep an attribute name looking like a normal attribute (obj.private_attr) from the outside while actually running validation logic through a getter and setter method underneath.
Cricket analogy: @property is like a scoreboard displaying "Run Rate: 8.2" as a simple number while underneath a getter recalculates it from overs and runs, and a setter validates that any manual override doesn't produce a negative rate.
Python has no true 'private' attributes enforced by the language — only conventions. A single underscore (_attr) is a polite request not to touch it directly; a double underscore (__attr) triggers name mangling (becoming _ClassName__attr), which discourages accidental access and prevents naming clashes in subclasses, but a determined caller can still access obj._ClassName__attr directly. Encapsulation in Python is fundamentally about discipline and clear signaling, not hard security.
4. Example
class BankAccount:
def __init__(self, owner, balance):
self.owner = owner
self.__balance = balance # name-mangled to _BankAccount__balance
@property
def balance(self):
return self.__balance
@balance.setter
def balance(self, amount):
if amount < 0:
raise ValueError("Balance cannot be negative")
self.__balance = amount
acc = BankAccount("Meera", 1000)
print(acc.balance)
acc.balance = 1500
print(acc.balance)
print(acc._BankAccount__balance) # mangled name still reachable directly
try:
acc.balance = -50
except ValueError as e:
print("Error:", e)5. Output
1000
1500
1500
Error: Balance cannot be negative6. Key Takeaways
- Encapsulation bundles data and behavior together and restricts uncontrolled outside access to internal state.
- Python has no enforced private access — only the _protected and __private naming conventions.
- Double-underscore names are name-mangled to _ClassName__name, discouraging but not preventing direct access.
- The @property decorator provides controlled, validated access to internal attributes with normal attribute syntax.
- Encapsulation in Python relies on convention and cooperative discipline rather than compiler-enforced access control.
Practice what you learned
1. What is the main goal of encapsulation?
2. What actually happens to an attribute named __balance inside a class BankAccount?
3. Does Python enforce truly private attributes the way Java's private keyword does?
4. What is the purpose of the @property decorator in the BankAccount example?
5. In the example, what happens when acc.balance = -50 is executed?
Was this page helpful?
You May Also Like
Abstraction in Python
How Python hides implementation complexity behind simple interfaces, using the abc module to enforce required methods.
Classes and Objects in Python
How Python's class statement defines a blueprint for objects, and how instances are created and used.
Magic (Dunder) Methods in Python
How Python's double-underscore methods let your classes plug into built-in syntax like print(), ==, len(), and hashing.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics