Python Notes
Learn encapsulation in Python — public, protected, and private attributes, name mangling, getter/setter methods, the @property decorator, and data validation patterns.
🧠 Real-World Analogy — ATM Machine
When you use an ATM, you only need to press buttons — you don't know how the circuits work inside, what mechanism dispenses cash, or how the balance is stored in the database. That's encapsulation — internal details are hidden, only a clean interface is exposed. This is Encapsulation — hiding internal implementation and exposing only the necessary interface.
🏷️ Access Modifiers in Python
| Notation | Type | Who Can Access |
|---|---|---|
name | Public | Everyone |
_name | Protected (convention) | Class + subclasses (convention only) |
__name | Private (name-mangled) | Only the class itself |
⚠️ Python doesn't enforce access control — it uses conventions and name mangling as guardrails.
✅ Example 1 — Public, Protected, Private
Output:
🏦 Example 2 — Bank Account with Getter/Setter
Output:
| + Deposited ₹10,000 | Balance | ₹10,000 |
| + Deposited ₹5,000 | Balance | ₹15,000 |
| - Withdrew ₹3,000 | Balance | ₹12,000 |
| + Deposited ₹2,000 | Balance | ₹14,000 |
| - Withdrew ₹500 | Balance | ₹13,500 |
| Account | Rahul Sharma |
| Balance | ₹13,500 |
| Owner | Rahul Sharma |
| Balance | ₹13,500 |
| Updated owner | Rahul Kumar Sharma |
| Error | Insufficient funds. Balance: ₹13,500 |
🎯 The @property Decorator (Pythonic Way)
The Pythonic way to create getters/setters in Python is the @property decorator. You can access it like an attribute, but validation runs internally.class Temperature:
def __init__(self, celsius=0):
self._celsius = celsius # use single underscore internally
@property
def celsius(self): # getter
return self._celsius
@celsius.setter
def celsius(self, value): # setter with validation
if value < -273.15:
raise ValueError(f"Temperature below absolute zero ({value}°C) is impossible!")
self._celsius = value
@celsius.deleter
def celsius(self): # deleter
print("Deleting temperature...")
del self._celsius
@property
def fahrenheit(self): # derived (read-only) property
return (self._celsius * 9/5) + 32
@property
def kelvin(self): # derived (read-only) property
return self._celsius + 273.15
def __str__(self):
return (f"{self._celsius}°C = {self.fahrenheit:.2f}°F = {self.kelvin:.2f} K")
t = Temperature(100)
print(t)
t.celsius = 37 # setter called — looks like attribute assignment!
print(t)
t.celsius = 0
print(t)
# Validation
try:
t.celsius = -300 # below absolute zero
except ValueError as e:
print(f"Error: {e}")
# Read-only: can't set fahrenheit directly
try:
t.fahrenheit = 200
except AttributeError as e:
print(f"Error: {e}")Output:
💊 Example 3 — Medicine Dosage System
Output:
| Paracetamol | 500mg × 4/day = 2000mg/day [✅ SAFE] |
| Ibuprofen | 400mg × 3/day = 1200mg/day [✅ SAFE] |
| Aspirin | 300mg × 6/day = 1800mg/day [✅ SAFE] |
| Error | Single dose must be 1–1000 mg. Got 1500 mg. |
| Error | Frequency must be 1–8 times/day. Got 12. |
🎯 __slots__ — Memory Optimization + Restriction
🔍 Name Mangling Deep Dive
class Parent:
def __init__(self):
self.__secret = "Parent's secret" # mangled to _Parent__secret
def reveal(self):
return self.__secret # works inside the class
class Child(Parent):
def __init__(self):
super().__init__()
self.__secret = "Child's secret" # mangled to _Child__secret (DIFFERENT!)
def reveal_child(self):
return self.__secret # accesses _Child__secret
p = Parent()
c = Child()
print(p.reveal()) # Parent's secret
print(c.reveal()) # Parent's secret (from Parent's reveal)
print(c.reveal_child()) # Child's secret
# Peek behind the curtain:
print(vars(c))
# {'_Parent__secret': "Parent's secret", '_Child__secret': "Child's secret"}🇮🇳 Hindi Explanation (हिंदी में समझें)
Encapsulation kya hai? Encapsulation means bundling data (attributes) and code (methods) into a single unit (class), and hiding internal details. Only the necessary interface should be exposed.
How to make things private in Python? Double underscore__varmakes an attribute private — Python internally changes its name (_ClassName__var). This is name mangling. Single underscore_varis just a convention ("please don't touch this") — no actual restriction.
Why do we use@property?@propertylets you have attribute-style access (obj.temp) with validation and computation — without method call syntax. This is Python's cleanest encapsulation pattern.
❓ Interview Questions
Q1: What is encapsulation? Why is it important?
Answer: Encapsulation is the bundling of data (attributes) and behavior (methods) within a class, while hiding internal implementation details. It's important because:
- Protects data integrity (validation in setters)
- Reduces complexity (only expose what's needed)
- Allows internal changes without breaking external code
- Prevents unintended modification of state
Q2: How does Python implement private attributes?
Answer: Python uses name mangling for double-underscore attributes. self.__balance becomes self._BankAccount__balance. This makes accidental access harder but doesn't truly prevent it. It's a strong convention rather than strict enforcement.
Q3: What is the difference between _x, __x, and x?
Answer:
x— Public: accessible from anywhere_x— Protected: convention signals "internal use / subclasses only"; Python doesn't enforce it__x— Private: name-mangled to_ClassName__x; harder to access from outside, not inherited by subclasses under the original name
Q4: What is the @property decorator? How is it different from a regular getter?
Answer: @property creates a managed attribute that is accessed like a plain attribute but calls a getter function under the hood. Benefits over regular get_x(): cleaner syntax (obj.x vs obj.get_x()), backward-compatible (can convert plain attributes to properties), and adds @x.setter and @x.deleter support.
Q5: What is __slots__? When should you use it?
Answer: __slots__ restricts which attributes an instance can have. Benefits: ~40% memory reduction (no __dict__ per object), faster attribute access. Use when creating millions of objects (e.g., data records, game entities). Downside: can't add new attributes dynamically; mixins/multiple inheritance becomes complex.
Q6: Can you access a private attribute from outside the class?
Answer: Technically yes — via name mangling: obj._ClassName__attr. But this is strongly discouraged. The mangling exists to prevent *accidental* access, not to provide true security. Python philosophy: "we are all consenting adults."
Q7: How do you make a read-only property?
Answer: Define only the @property getter without a @x.setter. Trying to assign to it raises AttributeError: can't set attribute.
🧪 Practice Exercises
- Person Card: Create a
Personclass withname(no empty string),age(0–120),email(must contain @) using@propertywith validation. - Stock Price: Create a
Stockclass withpriceproperty that must be positive and auto-records historical prices in a private list. - Circle: Create a
Circlewithradiusproperty. Auto-computeareaanddiameteras read-only properties. - Password Manager: Create a class where
passwordcan only be set (not read), stores a hash, and hasverify(pin)method. - Thermostat: Create a
Thermostatwheretemperaturecan be set in Celsius, read in both Celsius and Fahrenheit, with min/max clamping.
💡 Next Topic → Abstraction: Learn to use abstract classes,ABC,@abstractmethod, and design clean interfaces.
Exam Focus
Revise definitions, diagrams, examples, and short-answer points for Encapsulation in Python.
Interview Use
Prepare one clear explanation, one practical example, and one common mistake for this Python Master Course topic.
Search Terms
python-master-course, python master course, python, master, course, object, oriented, programming
Related Python Master Course Topics