Skip to main content

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 execute obj.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​

  1. 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.
  2. 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'

Loop 1: p → 0xB100
Loop 2: p → 0xB200

➔

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_payment implementation with specialized PhonePe UPI logic.

Line: checkout(payment_method, amount)​

  • Polymorphic Parameter: payment_method receives whatever object is passed into it.

Line: payment_method.process_payment(amount)​

  • Dynamic Dispatch: At runtime, Python looks up the exact class of payment_method and executes that class's custom process_payment code.

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/else type checking, making software easy to extend.

What's Next?​