Pillar 2: Polymorphism
The Second Pillar of Object-Oriented Programming is Polymorphism.
The word Polymorphism originates from Greek: Poly (many) + Morph (forms). In software engineering, Polymorphism means allowing different objects to respond to the exact same method call in their own unique way.
1. Learning Objective
By the end of this lesson, you will understand:
- What Polymorphism is and why it makes software flexible and extensible.
- How Method Overriding allows a child class to replace parent behavior.
- What Duck Typing ("If it walks like a duck...") means in Python.
- How to process lists of diverse objects using a single unified loop.
2. Why This Concept Exists
Imagine you are building a Payment Processing System for an e-commerce website that accepts Credit Cards, PayPal, and Crypto Payments.
Without Polymorphism, your code would be filled with messy if/else checks:
# Messy Code WITHOUT Polymorphism:
if payment_type == "credit_card":
pay_with_card(amount)
elif payment_type == "paypal":
pay_with_paypal(amount)
elif payment_type == "crypto":
pay_with_crypto(amount)
Every time you add a new payment method (e.g., Apple Pay), you would have to search and update dozens of if/else statements across your codebase!
With Polymorphism, every payment object implements a method named process_payment(amount). Your system simply calls payment.process_payment(amount) without caring which specific payment object it is handling.
3. Common Beginner Confusions
Confusion 1: "Do I need Inheritance to achieve Polymorphism in Python?"
- Answer: NO! Unlike static languages like C++ or Java, Python uses Duck Typing. As long as two unrelated objects both have a method named
make_sound(), Python can executeobj.make_sound()on both objects without requiring a common parent class.
Confusion 2: "What is Method Overriding?"
- Answer: Method Overriding occurs when a child class redefines a method that was already defined in its parent class. When called on a child object, Python executes the child's new version instead of the parent's old version.
4. Mental Model: Universal Remote Control & Duck Typing
-
Universal Remote Control (Polymorphism):
- Imagine a universal remote control with a single "Play" button.
- Point it at a TV object → Plays a video stream.
- Point it at a Speaker object → Plays an audio song.
- Point it at a DVD Player → Spins a laser disc.
- The action name (
play()) is identical, but the internal execution changes based on the target object.
-
Duck Typing Philosophy:
- "If it walks like a duck and quacks like a duck, it's a duck."
- Python does not check an object's formal class type. It only checks: "Does this object have the method I'm trying to call right now?"
5. Internal Python Execution Flow
Let's trace what happens when executing a polymorphic loop:
Code Executed:
for p in [PhonePe(), CreditCard()]:
p.process_payment(500)
↓ Execution Trace:
Iteration 1: p = PhonePe object (Address 0xB100)
1. Python executes p.process_payment(500).
2. Python looks up class of 0xB100 -> 'PhonePe'.
3. Python executes PhonePe.process_payment(0xB100, 500).
4. Output: "Processing ₹500 via PhonePe UPI."
Iteration 2: p = CreditCard object (Address 0xB200)
1. Python executes p.process_payment(500).
2. Python looks up class of 0xB200 -> 'CreditCard'.
3. Python executes CreditCard.process_payment(0xB200, 500).
4. Output: "Processing ₹500 via Credit Card Gateway."
6. Memory Visualization: Dynamic Polymorphic Dispatch
RAM Layout: Dynamic Method Dispatch via Object References
LOOP VARIABLE 'p'
➔
HEAP OBJECTS & CLASS METHODS
0xB100 (PhonePe) → runs PhonePe.process_payment()
0xB200 (CreditCard) → runs CreditCard.process_payment()
7. Python Code Implementation
# 1. Parent Base Class
class PaymentGateway:
def process_payment(self, amount):
print(f"Generic payment processing for ₹{amount}.")
# 2. Child Class 1: Method Overriding
class PhonePe(PaymentGateway):
def process_payment(self, amount):
print(f"Processing ₹{amount} securely via PhonePe UPI.")
# 3. Child Class 2: Method Overriding
class CreditCard(PaymentGateway):
def process_payment(self, amount):
print(f"Processing ₹{amount} via Credit Card Gateway.")
# 4. Unrelated Class (Duck Typing in action!)
class CryptoWallet:
def process_payment(self, amount):
print(f"Processing ₹{amount} equivalent on Blockchain network.")
# --- Polymorphic Function ---
def checkout(payment_method, amount):
# Calls .process_payment() regardless of object type!
payment_method.process_payment(amount)
# --- Execution ---
gateways = [PhonePe(), CreditCard(), CryptoWallet()]
print("--- Processing Bulk Orders ---")
for g in gateways:
checkout(g, 999)
Output:
--- Processing Bulk Orders ---
Processing ₹999 securely via PhonePe UPI.
Processing ₹999 via Credit Card Gateway.
Processing ₹999 equivalent on Blockchain network.
8. Line-by-Line Execution Analysis
Line: def process_payment(self, amount): inside PhonePe
- Method Overriding: Replaces the generic
PaymentGateway.process_paymentimplementation with specialized PhonePe UPI logic.
Line: checkout(payment_method, amount)
- Polymorphic Parameter:
payment_methodreceives whatever object is passed into it.
Line: payment_method.process_payment(amount)
- Dynamic Dispatch: At runtime, Python looks up the exact class of
payment_methodand executes that class's customprocess_paymentcode.
9. Before / After Method Overriding Comparison
Parent Definition (PaymentGateway)
def process_payment(self, amount):
print("Generic payment processing.")
Child Override (PhonePe)
def process_payment(self, amount):
print(f"Processing ₹{amount} via PhonePe UPI.") # Overrides Parent!
When PhonePe().process_payment() is called, Python executes the child's overridden method.
10. Illustration Prompt for Diagram Generation
Excalidraw Diagram Prompt: A hand-drawn diagram illustrating Polymorphism and Duck Typing in Python. On the left, draw a user calling function "checkout(payment_object, 999)". Draw 3 different object boxes in Heap RAM: "PhonePe Object", "CreditCard Object", "CryptoWallet Object". Inside each box, draw a common method plug labeled ".process_payment(999)". Show 3 execution arrows coming out of the single "checkout()" function, plugging into the 3 different object method plugs, producing 3 different formatted receipts at the bottom. White background, hand-drawn style, color palette: Purple (Caller), Green (Objects), Blue (Method Dispatch).
Quick Summary
- Polymorphism allows different object classes to share the exact same method interface while executing unique custom logic.
- Method Overriding occurs when a child class redefines a parent method.
- Duck Typing means Python evaluates objects based on whether they implement required methods, rather than their formal class inheritance tree.
- Polymorphism eliminates messy
if/elsetype checking, making software easy to extend.