Excellent.
DAY 18 is where Python’s Object-Oriented Programming model becomes crystal clear — not Java-style OOP, but Python’s dynamic, object-centric OOP.
Many people “use classes”.
Very few understand how Python actually resolves attributes and builds objects.
🧠 DAY 18 — OOP Fundamentals
(Classes, Instances & Attribute Lookup Model)
🔑 CORE IDEA OF DAY 18
In Python, classes are objects, instances are objects, and OOP is built on attribute lookup — not rigid blueprints.
Python OOP is dynamic, flexible, and runtime-driven.
1️⃣ Class vs Instance (NO CONFUSION AFTER TODAY)
class A:
pass
a = A()
A→ class objecta→ instance object- Both are objects in memory
type(A) # type
type(a) # A
🧠 Classes are created by type, just like instances are created by classes.
2️⃣ What Happens When You Create an Object
class A:
def __init__(self, x):
self.x = x
a = A(10)
Execution flow:
A.__new__()→ allocates memoryA.__init__()→ initializes object- Reference assigned to
a
⚠️ __init__ is NOT the constructor
It’s the initializer
3️⃣ Object Memory Model (CRITICAL)



Every instance contains:
instance
├── __dict__ → instance attributes
└── __class__ → reference to class
Every class contains:
class
├── __dict__ → class attributes & methods
└── __mro__ → method resolution order
4️⃣ Attribute Lookup Order (INTERVIEW GOLD)
When you access:
a.x
Python searches in this exact order:
a.__dict__A.__dict__- Parent classes (via MRO)
object.__dict__
If not found → AttributeError
🧠 This explains shadowing, overriding, and bugs
5️⃣ Instance Attribute vs Class Attribute (TRAP)
class A:
x = 10
a = A()
b = A()
a.x = 20
Results:
a.x → 20 (instance attribute)
b.x → 10 (class attribute)
Memory:
a.__dict__ = {'x': 20}
A.__dict__ = {'x': 10}
⚠️ Assignment creates instance attributes unless explicitly modifying class.
6️⃣ Why Mutable Class Attributes Are Dangerous 🔥
class A:
data = []
a = A()
b = A()
a.data.append(1)
print(b.data)
✔ [1]
Why?
datalives in class- Shared across all instances
🚨 This causes real production bugs
Correct pattern:
class A:
def __init__(self):
self.data = []
7️⃣ Methods Are Just Functions (BOUND vs UNBOUND)
class A:
def f(self):
pass
Access:
A.f # function (unbound)
a.f # method (bound)
What “bound” means:
selfis automatically attached
a.f() ≡ A.f(a)
🧠 Methods are descriptors — not special syntax.
8️⃣ self Is NOT a Keyword
class A:
def f(this):
print(this)
Works perfectly.
self is a convention, not a keyword.
9️⃣ Data Hiding in Python (Reality Check)
Python does NOT enforce private/protected.
class A:
def __init__(self):
self._x = 1
self.__y = 2
_x→ convention (“internal use”)__y→ name-mangled to_A__y
Not security. Just collision avoidance.
🔟 Why Python OOP Feels Different from Java
| Feature | Java | Python |
|---|---|---|
| Access control | Strict | Convention |
| Method binding | Compile-time | Runtime |
| Inheritance | Rigid | Flexible |
| Duck typing | ❌ | ✅ |
Python prioritizes behavior over hierarchy.
11️⃣ Composition vs Inheritance (Pythonic View)
Python prefers:
class Car:
def __init__(self, engine):
self.engine = engine
Over:
class Car(Engine):
...
Reason:
- Fewer inheritance chains
- Easier refactoring
- Cleaner MRO
🔥 INTERVIEW TRAPS (DAY 18)
Q1
class A:
x = 10
a = A()
print(a.x)
A.x = 20
print(a.x)
✔ 10
✔ 20
Q2
class A:
pass
a = A()
print(hasattr(a, "__dict__"))
✔ True
Q3
Why __init__ is not constructor?
✔ Object already exists
✔ It only initializes
✔ Allocation happens in __new__
🧠 DAY 18 MENTAL CHECKLIST
When debugging OOP:
- Where does attribute live?
- Is this instance or class attribute?
- Is method bound or unbound?
- Is shared state unintended?
- Is composition better here?
📝 DAY 18 ASSIGNMENT (MANDATORY)
1️⃣ Predict output (NO RUNNING):
class A:
x = []
a = A()
b = A()
a.x.append(1)
print(b.x)
2️⃣ Explain clearly:
- Attribute lookup order
- Difference between class and instance attributes
3️⃣ Whiteboard task:
Draw:
- Instance
- Class
__dict__- Attribute resolution path
🔜 DAY 19 PREVIEW (🔥🔥🔥)
DAY 19 — OOP Internals: MRO, type, Metaclasses & __new__
You’ll learn:
- How Python resolves multiple inheritance
- What metaclasses really are
- How classes are created
- When
__new__matters
When ready, say 👉 “START DAY 19”