Pillar 3: Encapsulation
The Third Pillar of Object-Oriented Programming is Encapsulation.
Encapsulation means bundling data (attributes) and methods together inside a single class container while restricting direct, unauthorized external access to sensitive variables.
1. Learning Objective
By the end of this lesson, you will understand:
- Why exposing public attributes directly (
account.balance = -9999) is dangerous. - How to define Private Attributes in Python using double underscores (
__attribute). - How Python's internal Name Mangling mechanism works in memory.
- How to use Getters and Setters with validation to protect object integrity.
2. Why This Concept Exists
Imagine a Banking System where anyone can write:
account = BankAccount("Sai", 5000)
# Unrestricted Public Access (DANGEROUS!)
account.balance = -9999999
Without Encapsulation, external code can corrupt internal object data without validation or security checks.
Encapsulation acts as a protective shield around an object's internal state. It forces external code to pass through approved Getter and Setter methods that validate inputs before making modifications.
3. Common Beginner Confusions
Confusion 1: "Is double underscore __balance truly 100% private in Python?"
- Answer: Conceptually yes, but physically in Python memory, Python uses Name Mangling. Adding double underscores transforms
__balanceinto_ClassName__balance(e.g.,_BankAccount__balance). Python does this to prevent accidental overrides, but it respects developer maturity ("We are all consenting adults here").
Confusion 2: "What is the difference between single underscore _var and double underscore __var?"
- Answer:
- Single Underscore
_var(Protected Convention): Hints to other developers: "This is internal; please don't touch it directly." Python does NOT mangle the name. - Double Underscore
__var(Private Name Mangling): Python actively mangles the name in memory, raising anAttributeErrorif accessed directly viaobj.__var.
- Single Underscore
4. Mental Model: ATM Machine Vault Keypad
Think of Encapsulation using the ATM Machine analogy:
- Inside the Vault (
__balance): The cash vault inside an ATM is private data. Bank customers are not allowed to open the vault door with a crowbar to grab cash directly (account.__balance). - The ATM Screen & Keypad (Getters/Setters): You must interact with approved, validated methods:
get_balance()→ Checks your PIN and reads the screen.deposit(amount)→ Checks ifamount > 0before adding cash to the vault.
5. Internal Python Execution Flow
Let's trace how Python mangles private attribute names in memory:
Code Executed: self.__balance = balance (inside class BankAccount)
↓ Name Mangling Trace:
1. Python sees double underscore prefix '__' before attribute name 'balance'.
2. Python inspects current class name -> 'BankAccount'.
3. Python rewrites key name into: _BankAccount__balance.
4. Python stores value 5000 inside _BankAccount__balance on Heap Object.
5. If external code calls account.__balance:
- Python looks for key '__balance' -> Not Found!
- Raises AttributeError: 'BankAccount' object has no attribute '__balance'.
6. Memory Visualization: Private Name Mangling Layout
RAM Layout: Protected Private Attribute (Name Mangling)
EXTERNAL DIRECT ACCESS
account.__balance
❌ AttributeError! (Key '__balance' hidden)
HEAP OBJECT (Address 0xC100)
owner: "Sai" (Public)
_BankAccount__balance: 5000 (Mangled Private)
7. Python Code Implementation
class BankAccount:
def __init__(self, owner, balance):
self.owner = owner
self.__balance = balance # Private Attribute (Name Mangled)
# 1. Getter Method: Controlled Read Access
def get_balance(self):
return self.__balance
# 2. Setter Method: Controlled Write Access with Input Validation
def deposit(self, amount):
if amount > 0:
self.__balance += amount
print(f"Deposited ₹{amount}. New Balance: ₹{self.__balance}")
else:
print("Error: Deposit amount must be positive!")
def withdraw(self, amount):
if 0 < amount <= self.__balance:
self.__balance -= amount
print(f"Withdrew ₹{amount}. Remaining Balance: ₹{self.__balance}")
else:
print("Error: Invalid transaction or Insufficient Funds!")
# --- Execution ---
acc = BankAccount("Sai", 5000)
# Attempting direct private access:
# print(acc.__balance) # Raises AttributeError!
# Controlled Read via Getter
print("Current Balance:", acc.get_balance())
# Controlled Write via Setter (Validation Success)
acc.deposit(1500)
# Controlled Write via Setter (Validation Failure)
acc.deposit(-500)
acc.withdraw(10000)
Output:
Current Balance: 5000
Deposited ₹1500. New Balance: ₹6500
Error: Deposit amount must be positive!
Error: Invalid transaction or Insufficient Funds!
8. Line-by-Line Execution Analysis
Line: self.__balance = balance
- Name Mangling: Python internally transforms
__balanceinto_BankAccount__balanceon Heap object.
Line: acc.deposit(-500)
- Validation Defense: Setter method checks
if amount > 0. Since-500 > 0isFalse, the invalid deposit is rejected, preserving internal data integrity.
9. Before / After Direct Access vs Encapsulated Access
Unencapsulated (Public Attribute)
acc.balance = -500 # Object data corrupted instantly!
Encapsulated (Private Attribute + Setter)
acc.deposit(-500) # Validation fails: "Error: Amount must be positive!" Data remains safe.
10. Illustration Prompt for Diagram Generation
Excalidraw Diagram Prompt: A hand-drawn diagram illustrating Encapsulation and Name Mangling in Python. Draw a thick circular shield boundary labeled "Class BankAccount (Encapsulation Boundary)". Inside the shield, draw a locked safe box labeled "Private Attribute: __balance (Mangled to _BankAccount__balance = 5000)". On the outside of the shield, draw an unauthorized user arrow labeled "acc.__balance" hitting the shield wall and bouncing off with a red "AttributeError" collision spark. Draw two approved door keypads labeled "get_balance()" and "deposit(amount)" with green arrows passing through validation checkpoints into the safe box. White background, clean hand-drawn style, colors: Red (Blocked Access), Green (Approved Gateways), Blue (Shield Boundary).
Quick Summary
- Encapsulation bundles data and methods while restricting direct external access to sensitive attributes.
- Use double underscores
__attributeto make attributes private. - Python protects private attributes via Name Mangling (
_ClassName__attribute). - Getters and Setters provide controlled, validated interfaces to read and update private object data safely.