Why Object oriented Programming needed¶
Functions describe how. Classes describe who.**¶
Functions: “Do this action on that data.”
Classes: “This thing knows how to do its own actions.”
Example:
deposit(account, amount)means “Tell some external function to modify this account.”
But
account.deposit(amount)means “The account itself knows how to update its balance.”
The difference is agency — in OOP, data becomes active. This change — from data being passive to data being intelligent — is the essence of why OOP exists.
Classes let you build your own data types.**¶
Students already know built-in types:
int, float, list, dict.
Ask:
“What if you could create your own type — like
BankAccountorStudent— that behaves just like built-ins?”
class Student:
def __init__(self, name, marks):
self.name = name
self.marks = marks
def average(self):
return sum(self.marks) / len(self.marks)Now:
s = Student("Alice", [80, 90, 85])
print(s.average())You just created a new data type — not a function that acts on a dict.
That’s as fundamental as inventing list or set yourself.
It’s not about shorter code — it’s about creating reusable, meaningful data types that model your world.
Classes let your code mirror reality.**¶
Without classes:
deposit(bank, customer, amount)
withdraw(bank, customer, amount)With classes:
customer.deposit(amount)
customer.withdraw(amount)This reads like English. The code is now a story, not a sequence of steps.
You’re no longer “programming instructions.” You’re “building a world” that behaves logically.
That’s what all modern frameworks (like Django, Flask, PyTorch, TensorFlow) do.
Classes let you scale from 10 lines to 10,000.**¶
Ask:
“What happens when your system grows?”
In the function version of the shop system:
Every function needs a reference to every piece of data.
Naming conflicts appear.
Adding a new feature means editing 10 functions.
In the class version:
Each class manages itself.
Adding a new feature means editing one class or subclass.
You can reuse the rest untouched.
Scalability isn’t visible in small programs — it’s a design strength.
Classes turn programs into living systems.**¶
Imagine your students build a small simulation:
class Animal:
def speak(self):
pass
class Dog(Animal):
def speak(self):
print("Woof!")
class Cat(Animal):
def speak(self):
print("Meow!")
for animal in [Dog(), Cat()]:
animal.speak()They’ll suddenly see it — each object knows how to act on its own.
You don’t write if type == "dog": print("Woof!") anymore.
You just let the objects behave.
That’s a mental revolution — from control everything manually to let things self-manage.
Classes let teams build together.**¶
In real software development:
One person writes a
Shopclass.Another person writes a
Customerclass.Another writes a
Franchiseclass.
Each knows the interface, not the implementation. They don’t step on each other’s toes. That’s how large projects work.
You can’t coordinate teams with only functions and dictionaries — there’s no “ownership” structure.
Classes are the foundation of every major Python framework**¶
When students eventually learn:
Pandas → DataFrame class
Scikit-learn → Model class (with
.fit()and.predict())Django → Model and View classes
Matplotlib → Figure and Axes classes
…they’ll see OOP everywhere.
Every powerful tool they use in Python is built with classes.
So the message should be:
“You might not use classes every day, but every professional Python tool you’ll ever use depends on them.”
In short¶
Functions make small scripts tidy. Classes make large systems possible.
Functions = steps to perform a task.
Classes = actors that know their roles in the world.
Or as I often tell students:
“When your code needs to remember things, grow, and behave — you stop writing steps, and start creating objects.”
Stage 1 — The “Stupid” Function-Based World¶
“Let’s say we want animals that can speak. Simple, right? We’ll just use functions.”
## Function-based version
def speak(animal_type):
if animal_type == "dog":
print("Woof!")
elif animal_type == "cat":
print("Meow!")
elif animal_type == "bird":
print("Tweet!")
else:
print("Unknown animal sound.")
speak("dog")
speak("cat")
speak("bird")Looks fine… until you add more animals.
Now add 10 more animals¶
def speak(animal_type):
if animal_type == "dog":
print("Woof!")
elif animal_type == "cat":
print("Meow!")
elif animal_type == "bird":
print("Tweet!")
elif animal_type == "lion":
print("Roar!")
elif animal_type == "snake":
print("Hiss!")
elif animal_type == "cow":
print("Moo!")
elif animal_type == "duck":
print("Quack!")
elif animal_type == "frog":
print("Ribbit!")
elif animal_type == "horse":
print("Neigh!")
elif animal_type == "sheep":
print("Baa!")
else:
print("Unknown animal sound.")Now ask:
“What if we also want animals to eat(), sleep(), or move()?” “Will you create a
move(animal_type),eat(animal_type),sleep(animal_type)function with the same long if-elif chain every time?”
It’s clearly ugly, repetitive, and fragile.
If you misspell "dog" somewhere, it silently breaks.
Stage 2 — Enter Classes (and the World Becomes Logical)¶
You say:
“Let’s let animals take care of themselves.”
class Animal:
def speak(self):
print("Some generic animal sound")
def move(self):
print("The animal moves")
def eat(self):
print("The animal eats food")Now, specialized animals inherit from Animal and override only what changes.
class Dog(Animal):
def speak(self):
print("Woof!")
class Cat(Animal):
def speak(self):
print("Meow!")
class Bird(Animal):
def speak(self):
print("Tweet!")
def move(self):
print("The bird flies")
class Snake(Animal):
def speak(self):
print("Hiss!")
def move(self):
print("The snake slithers")Stage 3 — Show the Power (Dynamic Behavior)¶
Now all animals share the same interface, and you can treat them uniformly.
animals = [Dog(), Cat(), Bird(), Snake()]
for animal in animals:
animal.speak()
animal.move()
animal.eat()
print("-" * 30)Output¶
Woof!
The animal moves
The animal eats food
------------------------------
Meow!
The animal moves
The animal eats food
------------------------------
Tweet!
The bird flies
The animal eats food
------------------------------
Hiss!
The snake slithers
The animal eats food
------------------------------Stage 4 — Add New Animals Instantly¶
You now say:
“What if I add an Elephant? Do I need to touch the existing code?”
In the function version, yes — you’d edit a huge if-elif block (and risk breaking others).
In the class version, you simply add a new class.
class Elephant(Animal):
def speak(self):
print("Trumpet!")
def move(self):
print("The elephant walks heavily")And it just works:
animals.append(Elephant())
for a in animals:
a.speak()Output:
Woof!
Meow!
Tweet!
Hiss!
Trumpet!No if-elif, no touching old code.
That’s inheritance + polymorphism in action.
Stage 5 — Add New Behavior (Extensibility)¶
Now, let’s add a new behavior — sleep() — without breaking anything.
class Animal:
def speak(self):
print("Some generic sound")
def move(self):
print("The animal moves")
def eat(self):
print("The animal eats food")
def sleep(self):
print("Zzz...")All animals inherit it automatically. You didn’t touch the subclasses — they already know how to sleep.
Dog().sleep()
Cat().sleep()Output:
Zzz...
Zzz...That’s true scalability — the system grows without breaking.
Stage 6 — Real-World Magic: Collections of Mixed Objects¶
Now show the mind-blow moment:
“We can mix all animals in one list and make them all act, without even checking their types.”
zoo = [Dog(), Cat(), Bird(), Snake(), Elephant()]
for animal in zoo:
animal.speak()Python automatically calls the right method for each animal. This is polymorphism — many forms, one interface.
** Mindset Transformation**¶
“In the functions world, you control everything manually — like a puppeteer moving strings. In the class world, you give each puppet a brain — they act on their own.”
That’s why OOP is not about “grouping functions.” It’s about creating intelligent, self-aware entities that manage their own data and actions.
** Bonus: Evolution Example**¶
Let’s add another layer of inheritance — subclasses of Bird:
class Parrot(Bird):
def speak(self):
print("Polly wants a cracker!")
class Eagle(Bird):
def move(self):
print("The eagle soars high in the sky")Now:
zoo = [Parrot(), Eagle(), Dog()]
for a in zoo:
a.speak()
a.move()Output:
Polly wants a cracker!
The bird flies
guess sound?
The eagle soars high in the sky
Woof!
The animal movesAnd you can say:
“See — even Birds can have children with different personalities! That’s inheritance working across generations.”
Interactive Pyodide Notebook — Jupyter-Style CodeMirror Version (4 Cells)¶
This notebook runs live Python in your browser using Pyodide with real-time syntax highlighting and indentation via CodeMirror.
Features:
Jupyter-like Python highlighting and indentation
Restartable Pyodide kernel
Resizable editors and outputs
Works on mobile (touch-resize + virtual keyboard)
Dark/light theme aware
<div class='resize-handle cell-resize'></div>
<div>
<button class='py-run'>▶ Run</button>
<button class='py-clear'>Clear</button>
<span class='run-status'></span>
</div>
<div class='output-wrapper' style='display:none;'>
<pre class='py-out' style='height:100px;'></pre>
<div class='resize-handle output-resize'></div>
</div>
<div style='text-align:center;padding:8px;'>
<button class='py-add-cell'>➕ Add Cell Below</button>
</div>
`;
afterElement.parentNode.insertBefore(newCell,afterElement.nextSibling);
window.wireInteractiveCells();
// Wire the new "Add Cell" button
const addBtn=newCell.querySelector('.py-add-cell');
addBtn&&addBtn.addEventListener('click',()=>window.addNewCell(newCell));};
// ---------- Initialize on DOM Ready ---------- document.addEventListener(‘DOMContentLoaded’,async()=>{ // Start loading Pyodide window.loadPyodideOnce();
// Wire all existing cells
await window.wireInteractiveCells();
// Add "Add Cell" buttons to existing cells
document.querySelectorAll('.py-cell').forEach(cell=>{
if(!cell.querySelector('.py-add-cell')){
const addBtnDiv=document.createElement('div');
addBtnDiv.style.cssText='text-align:center;padding:8px;';
addBtnDiv.innerHTML='<button class="py-add-cell">➕ Add Cell Below</button>';
cell.appendChild(addBtnDiv);
const addBtn=addBtnDiv.querySelector('.py-add-cell');
addBtn.addEventListener('click',()=>window.addNewCell(cell));
}
});
// Restart kernel button
const restartBtn=document.getElementById('restart-pyodide');
const statusSpan=document.getElementById('kernel-status');
restartBtn&&restartBtn.addEventListener('click',async()=>{
statusSpan.textContent='🔄 Restarting kernel...';
statusSpan.style.color='orange';
restartBtn.disabled=true;
delete window.pyodide;
window.pyodideReadyPromise=null;
await window.loadPyodideOnce();
statusSpan.textContent='✅ Kernel restarted';
statusSpan.style.color='var(--pst-color-primary,#2196f3)';
restartBtn.disabled=false;
});}); })();