Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

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 BankAccount or Student — 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 Shop class.

  • Another person writes a Customer class.

  • Another writes a Franchise class.

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 moves

And 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


🔄 Restart Kernel ⏳ Loading Pyodide…
  <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;
});

}); })();

Cell 1 — Student Class Example

class Student: def __init__(self, name, marks): self.name = name self.marks = marks
def average(self):
    return sum(self.marks) / len(self.marks)

s = Student(“Alice”, [80, 90, 85]) print(f"{s.name}'s average:", s.average())

▶ Run Clear

Exercises

Exercise