2.1.12OOP Fundamentals

Polymorphism — duck typing, same interface different behavior

1,840 words8 min readdifficulty · medium6 backlinks

WHAT is polymorphism?

There are two flavors you must separate in your head:

Kind Idea Python example
Subtype / inheritance A subclass overrides a parent method Dog(Animal) overrides speak()
Duck typing Any object that has the method works — no inheritance needed anything with .speak()

WHY does this work in Python? (first principles)

So polymorphism in Python is not a special feature bolted on — it falls out of dynamic attribute lookup. There is no compile-time type contract to satisfy.


HOW to use it — building from scratch

Start with the most general, type-agnostic version (pure duck typing):

def make_it_speak(thing):
    return thing.speak()        # we only require: thing has .speak()

Now give it many "ducks":

class Dog:
    def speak(self): return "Woof"
 
class Cat:
    def speak(self): return "Meow"
 
class Robot:                      # NOT related to Dog/Cat at all
    def speak(self): return "Beep boop"
 
for t in (Dog(), Cat(), Robot()):
    print(make_it_speak(t))      # Woof / Meow / Beep boop

The inheritance flavor (override)

class Animal:
    def speak(self):
        raise NotImplementedError   # base "promises" the interface
 
class Dog(Animal):
    def speak(self): return "Woof"  # override
 
class Cat(Animal):
    def speak(self): return "Meow"  # override
Figure — Polymorphism — duck typing, same interface different behavior

Worked Example 1 — A len-like total cost

class Book:
    def __init__(self, price): self.price = price
 
class Cart:
    def __init__(self, items): self.items = items
    def total(self):
        return sum(i.price for i in self.items)

Why this step? total() works for Book, but also for any object with a .price attribute — a Toy, a Subscription, etc. We coded an interface (has .price), not a type.

Worked Example 2 — Polymorphic area()

import math
class Circle:
    def __init__(self, r): self.r = r
    def area(self): return math.pi * self.r**2
 
class Square:
    def __init__(self, s): self.s = s
    def area(self): return self.s**2
 
def total_area(shapes):
    return sum(s.area() for s in shapes)
 
total_area([Circle(1), Square(2)])   # π·1² + 2² = π + 4 ≈ 7.1416

Why this step? total_area is written once. The same call s.area() runs πr2\pi r^2 for a circle and s2s^2 for a square — same interface, different behavior. Add Triangle later → zero changes to total_area.

Worked Example 3 — Built-in polymorphism (+)

1 + 2          # 3        (int.__add__)
"a" + "b"      # "ab"     (str.__add__)
[1] + [2]      # [1, 2]   (list.__add__)

Why this step? + is the same operator name dispatching to a different __add__ per type. This is operator polymorphism — even Python's syntax is duck-typed (it just calls __add__).



Recall Feynman: explain to a 12-year-old

Imagine a TV remote with one big red "GO" button. Press it on the car remote → the car drives. Press the same button on the drone remote → the drone flies. Same button, different machine, different result. You don't need to know how the machine works inside — you just press GO and it does its thing. In code, "GO" is the method name like speak(), and the machine is the object. As long as a machine has a GO button, your remote works with it — even a brand-new toy invented tomorrow.



Flashcards

What does "polymorphism" literally translate to?
"Many forms" — one interface, many behaviors.
Define duck typing in one line.
If an object has the method/attribute you use, it works — Python checks behavior, not class.
Does Python require inheritance for polymorphism?
No. Duck typing needs no shared base class; method lookup is dynamic at runtime.
What happens at runtime when you call x.foo()?
Python searches foo along type(x).__mro__; calls the first found, else raises AttributeError.
Why is isinstance-checking everywhere an anti-pattern for polymorphism?
It rejects new compatible types, breaking extensibility; prefer EAFP / hasattr capability checks.
Name the two flavors of polymorphism.
Subtype (inheritance + override) and duck typing (interface-based, no inheritance).
Why is + an example of polymorphism?
Same operator dispatches to a different __add__ per type (int adds, str/list concatenate).
What's the difference between overriding and overwriting a parent method?
Override affects only that subclass's lookup; the parent and siblings keep their own version.
EAFP stands for?
Easier to Ask Forgiveness than Permission — just call, catch the error if it fails.
Why does writing total_area(shapes) once support shapes invented later?
It only relies on the .area() interface, so any future object with area() plugs in unchanged.

Connections

  • Inheritance — overriding and super()
  • Abstraction — interfaces and abstract base classes (ABC)
  • Dunder methods — __add__, __len__, __str__
  • Method Resolution Order (MRO)
  • EAFP vs LBYL — Pythonic error handling
  • Encapsulation — bundling data and behavior
  • Liskov Substitution Principle (SOLID)

Concept Map

means

enabled by

walks

not found

has flavor

has flavor

uses

needs inheritance

checks

falls out of

gives

no inheritance needed

Polymorphism

One interface many behaviors

Dynamic attribute lookup

MRO method lookup

Subtype polymorphism

Duck typing

Override parent method

Implicit interface .speak

Extensibility without rewrite

AttributeError if missing

Hinglish (regional understanding)

Intuition Hinglish mein samjho

Dekho, polymorphism ka matlab hai "ek hi naam, alag-alag behavior". Tum thing.speak() likhte ho, par agar thing ek Dog hai to "Woof" aata hai, Cat hai to "Meow", aur Robot hai to "Beep boop". Call same hai, lekin result object ke type pe depend karta hai. Yeh OOP ka sabse powerful idea hai kyunki tum ek baar code likho aur woh future ke naye objects ke saath bhi chal jaaye — bina rewrite kiye.

Duck typing Python ka style hai: "agar woh duck ki tarah quack karti hai, to woh duck hai." Matlab Python yeh nahi poochta ki object kis class ka hai — woh sirf yeh dekhta hai ki object ke paas woh method/attribute hai ya nahi jo tum use kar rahe ho. Isliye Python me polymorphism ke liye inheritance zaroori nahi. Robot ka Dog se koi rishta na ho phir bhi, agar uske paas speak() hai to tumhara function chal jaayega.

Andar se hota kya hai? Jab tum x.speak() likhte ho, Python runtime pe x ke type ke MRO (method resolution order) me speak dhoondhta hai, mil gaya to call kar deta hai, nahi mila to AttributeError. Kahin bhi "kya x ek Animal hai?" nahi poochta. Bas yahi dynamic lookup polymorphism ko possible banata hai.

Ek common galti: log har jagah isinstance check lagate hain — yeh polymorphism ko tod deta hai, kyunki naye compatible types reject ho jaate hain. Better hai EAFP — pehle method call karo, fail ho to handle karo, ya hasattr se capability check karo. Yaad rakho: Quack > Class — kaam karna important hai, class ka naam nahi.

Go deeper — visual, from zero

Test yourself — OOP Fundamentals

Connections