Home Course Roadmap
Home Python Course Lesson 46
Lesson 46 · Object-Oriented Programming 45–55 minutes Intermediate

Python Polymorphism

Learn how different objects can respond to the same method or operation in different ways. Polymorphism is one of the most powerful ideas in object-oriented programming and helps you build flexible, reusable Python programs.

Object-Oriented Programming
0%
01

Learning Objectives

By the end of this lesson, you will understand how polymorphism allows different objects to be treated through a common interface while producing different behavior.

You will learn how to:

  • Understand the meaning of polymorphism.
  • Use the same method name with different classes.
  • Understand method overriding.
  • Use polymorphism with inheritance.
  • Understand Python's duck typing approach.
  • Recognize polymorphism in built-in functions.
  • Use polymorphism in practical application design.
  • Design flexible systems without unnecessary if/elif checks.
02

What Is Polymorphism?

The word polymorphism means "many forms."

In programming, polymorphism means that the same interface, method, function, or operation can work with different types of objects and produce behavior appropriate to each object.

Simple idea: Different objects can respond to the same operation in their own way.

For example, imagine that both a dog and a cat have a method called speak().

Calling speak() on a dog might produce Woof!, while calling it on a cat might produce Meow!.

03

Why Polymorphism Matters

Without polymorphism, a program may need to constantly check what type of object it is working with.

For example, you could write code such as:

Without Polymorphism
if animal_type == "dog":
    print("Woof!")
elif animal_type == "cat":
    print("Meow!")
elif animal_type == "bird":
    print("Tweet!")

This approach becomes difficult to maintain when many object types are introduced.

With polymorphism, each object can provide its own implementation of a common operation.

With Polymorphism
for animal in animals:
    animal.speak()

The calling code does not need to know whether animal is a dog, cat, bird, or another compatible object.

04

Same Interface, Different Behavior

One of the easiest ways to understand polymorphism is to imagine several objects that provide the same method name.

Same Method, Different Behavior
class Dog:
    def speak(self):
        print("Woof!")


class Cat:
    def speak(self):
        print("Meow!")


animals = [Dog(), Cat()]

for animal in animals:
    animal.speak()
Output
Woof!
Meow!

Notice that the loop does not contain a separate condition for dogs and cats.

It simply says:

Call speak(). Each object decides how that operation should behave.
05

Polymorphism with Functions

A function can work with different objects as long as those objects provide the operation the function needs.

Function Example
class Dog:
    def speak(self):
        print("Woof!")


class Cat:
    def speak(self):
        print("Meow!")


def make_sound(animal):
    animal.speak()


make_sound(Dog())
make_sound(Cat())
Output
Woof!
Meow!

The function make_sound() does not need to know the exact class of the object.

It only assumes that the object provides a speak() method.

06

Polymorphism with Methods

Different classes can implement a method with the same name. When the method is called, Python uses the implementation belonging to the actual object.

Different Implementations
class Car:
    def move(self):
        print("Car is driving.")


class Boat:
    def move(self):
        print("Boat is sailing.")


class Plane:
    def move(self):
        print("Plane is flying.")


vehicles = [Car(), Boat(), Plane()]

for vehicle in vehicles:
    vehicle.move()
Output
Car is driving.
Boat is sailing.
Plane is flying.

The same method call:

vehicle.move()

produces different behavior depending on the object.

07

Method Overriding and Polymorphism

Method overriding occurs when a child class provides its own implementation of a method inherited from a parent class.

Method Overriding
class Animal:
    def speak(self):
        print("Some animal sound.")


class Dog(Animal):
    def speak(self):
        print("Woof!")


class Cat(Animal):
    def speak(self):
        print("Meow!")


animals = [Dog(), Cat()]

for animal in animals:
    animal.speak()
Output
Woof!
Meow!

Both objects are related to the same parent class, but each child class provides its own version of speak().

Important: Method overriding is one of the most common ways polymorphism appears in object-oriented Python programs.
08

Polymorphism with Inheritance

Inheritance and polymorphism often work together.

A parent class can define a common interface while child classes customize the behavior.

Inheritance + Polymorphism
class Animal:

    def speak(self):
        print("Some animal sound.")


class Dog(Animal):

    def speak(self):
        print("Woof!")


class Cat(Animal):

    def speak(self):
        print("Meow!")


class Cow(Animal):

    def speak(self):
        print("Moo!")


animals = [
    Dog(),
    Cat(),
    Cow()
]

for animal in animals:
    animal.speak()
Output
Woof!
Meow!
Moo!

The loop works with the parent-level concept of an Animal, while each child supplies its own implementation.

09

Duck Typing

Python is famous for its use of duck typing.

Duck typing focuses on what an object can do rather than what class it belongs to.

The idea: "If it behaves like the required object, Python can often use it."

Python does not necessarily require us to check:

if type(obj) == SomeClass:

Instead, we can simply attempt to use the required operation.

10

Practical Duck Typing

Consider a bird and an airplane.

They are completely different types of objects, but both can provide a fly() method.

Duck Typing Example
class Bird:

    def fly(self):
        print("Bird is flying.")


class Airplane:

    def fly(self):
        print("Airplane is flying.")


def start_flying(obj):
    obj.fly()


start_flying(Bird())
start_flying(Airplane())
Output
Bird is flying.
Airplane is flying.

The function does not care whether the object is a Bird or an Airplane.

It only requires that the object provide fly().

Important: Duck typing does not mean Python ignores errors. If the supplied object does not provide the required operation, an exception such as AttributeError may occur.
11

Polymorphism with Built-in Functions

You have already been using polymorphism in Python without necessarily realizing it.

Many built-in functions work with different types of objects.

For example, len() works with strings, lists, tuples, dictionaries, sets, and many other objects.

Built-in Polymorphism
print(len("Python"))
print(len([1, 2, 3]))
print(len({"a": 1, "b": 2}))
Output
6
3
2

The same function, len(), behaves appropriately for different object types.

12

The len() Function and Different Objects

The len() function is a useful real-world demonstration of polymorphic behavior.

Different Data Types
name = "Adnan"
numbers = [10, 20, 30, 40]
courses = ("Python", "SQL", "Power BI")
student = {
    "name": "Adnan",
    "age": 25
}

print(len(name))
print(len(numbers))
print(len(courses))
print(len(student))
Output
5
4
3
2

The function call remains the same:

len(object)

But the result depends on the object being supplied.

13

Operator Polymorphism

Python operators can also behave differently depending on the types of operands.

Consider the + operator.

The + Operator
print(2 + 3)

print("Py" + "Learn")

print([1, 2] + [3, 4])
Output
5
PyLearn
[1, 2, 3, 4]

The same operator produces different behavior:

  • Numbers are added.
  • Strings are concatenated.
  • Lists are combined.
This is another practical example of polymorphic behavior built into Python.
14

Polymorphism with Custom Classes

You can design your own classes to follow a common interface.

For example, suppose different payment classes all provide a pay() method.

Custom Polymorphism
class CreditCardPayment:

    def pay(self, amount):
        print(f"Paid ${amount} using credit card.")


class BankTransfer:

    def pay(self, amount):
        print(f"Paid ${amount} using bank transfer.")


class CashOnDelivery:

    def pay(self, amount):
        print(f"Paid ${amount} using cash on delivery.")


payments = [
    CreditCardPayment(),
    BankTransfer(),
    CashOnDelivery()
]

for payment in payments:
    payment.pay(500)
Output
Paid $500 using credit card.
Paid $500 using bank transfer.
Paid $500 using cash on delivery.

The processing code does not need to determine the payment type.

payment.pay(500)

Each object handles the operation according to its own implementation.

15

Common Method Names as Interfaces

In Python, a common method name can act like a simple interface between different objects.

For example, a reporting system might expect every report object to provide a method called generate().

Common Interface
class SalesReport:

    def generate(self):
        print("Generating sales report...")


class InventoryReport:

    def generate(self):
        print("Generating inventory report...")


class CustomerReport:

    def generate(self):
        print("Generating customer report...")


def create_report(report):
    report.generate()


create_report(SalesReport())
create_report(InventoryReport())
create_report(CustomerReport())
Output
Generating sales report...
Generating inventory report...
Generating customer report...

This design makes it easier to add new report types later.

16

Real-World Examples

Polymorphism is especially useful in applications where several objects need to perform the same general task in different ways.

E-commerce Payment System

Different payment methods can expose the same pay() method.

  • Credit card
  • Bank transfer
  • Cash on delivery
  • Digital wallet

Notification System

Different notification services can provide send().

  • Email
  • SMS
  • Push notification

Employee System

Different employee types could provide a common calculate_salary() method.

Data Export System

Different exporters could implement:

export()

Possible implementations include CSV, JSON, Excel, or PDF.

Design principle: When multiple objects perform the same conceptual task, a common interface can make your program easier to extend and maintain.
17

Benefits and Limitations

Benefits

  • Reduces repetitive conditional logic.
  • Makes code easier to extend.
  • Encourages reusable designs.
  • Improves flexibility.
  • Supports cleaner object-oriented architecture.
  • Makes systems easier to maintain.

Limitations

  • Too much abstraction can make code difficult to understand.
  • Different classes still need compatible interfaces.
  • Duck typing can produce runtime errors if assumptions are wrong.
  • Overly complicated inheritance structures can become difficult to maintain.
Good design matters: Do not introduce polymorphism simply because you can. Use it when it makes the design clearer, more flexible, or easier to extend.
18

Common Mistakes

1. Confusing Polymorphism with Inheritance

Inheritance and polymorphism are related, but they are not the same concept.

2. Assuming Objects Must Have the Same Class

Duck typing allows unrelated classes to work with the same function if they provide the required behavior.

3. Forgetting the Required Method

Problem
class Car:
    def drive(self):
        print("Driving")


def start(obj):
    obj.fly()


start(Car())

This produces an AttributeError because Car does not provide fly().

4. Overusing Type Checks

Constantly writing large chains of:

if type(obj) == ...
elif type(obj) == ...
elif type(obj) == ...

may indicate that a common interface or polymorphic design could be useful.

5. Making the Interface Inconsistent

If one class expects pay() while another uses make_payment(), the calling code cannot treat them consistently without additional logic.

19

Practice Exercises

Try these exercises without looking at the solution first.

Exercise 1 — Animals

Create Dog, Cat, and Cow classes. Give each a speak() method.

Exercise 2 — Vehicles

Create Car, Boat, and Airplane classes. Give each a move() method with different output.

Exercise 3 — Functions

Write a function called make_sound() that accepts any object having a speak() method.

Exercise 4 — Duck Typing

Create two unrelated classes that both implement run(). Write a function that calls run().

Exercise 5 — Payments

Create three payment classes with the same pay() method.

Exercise 6 — Reports

Create several report classes that all implement generate().

Exercise 7 — Inheritance

Create a parent Employee class and child classes that override work().

Exercise 8 — E-commerce

Create different delivery classes with a common deliver() method.

20

Mini Challenge — Payment Processing System

Build a simple polymorphic payment processing system.

Your Mission

Design a payment system where different payment methods can be processed using the same function.

Requirements

  1. Create a class called CreditCardPayment.
  2. Create a class called BankTransfer.
  3. Create a class called CashOnDelivery.
  4. Give every class a method: pay(amount).
  5. Create a function: process_payment(payment, amount).
  6. The function should simply call payment.pay(amount).
  7. Test all three payment methods.
Starter Structure
class CreditCardPayment:

    def pay(self, amount):
        print(f"Paid ${amount} using credit card.")


class BankTransfer:

    def pay(self, amount):
        print(f"Paid ${amount} using bank transfer.")


class CashOnDelivery:

    def pay(self, amount):
        print(f"Paid ${amount} using cash on delivery.")


def process_payment(payment, amount):
    payment.pay(amount)


process_payment(CreditCardPayment(), 1000)
process_payment(BankTransfer(), 2500)
process_payment(CashOnDelivery(), 750)
Extension Challenge: Add a DigitalWallet class without changing the process_payment() function.

If your new payment class works without changing the processing function, you have successfully demonstrated polymorphism.

21

Quick Knowledge Check

Test your understanding before moving to the next lesson.

1. What does polymorphism mean?

2. What can different objects provide for polymorphic code?

3. Which concept allows child classes to provide their own version of a method?

4. What does duck typing focus on?

5. What happens when this code runs?

print(len("Python"))

6. Which operator demonstrates polymorphic behavior?

7. Why is polymorphism useful?

8. Which method could be used as a common interface for payment classes?

9. What error might occur if an object does not provide a required method?

10. What is a major advantage of polymorphism?

22

Lesson Summary

Polymorphism allows different objects to respond to the same operation in different ways.

  • Polymorphism means many forms.
  • Different objects can provide the same method with different behavior.
  • Method overriding is a common form of polymorphism with inheritance.
  • Python supports duck typing, focusing on what an object can do.
  • Built-in functions such as len() work with many different object types.
  • Operators such as + can behave differently depending on operand types.
  • Custom classes can share common interfaces such as pay(), send(), or generate().
  • Polymorphism reduces unnecessary type-specific conditional logic.
  • Good polymorphic design makes applications easier to extend and maintain.
Key takeaway: Instead of asking, "What type of object is this?", good polymorphic code often asks, "What can this object do?"

Ready to move forward?

Mark this lesson complete after you understand polymorphism, method overriding, duck typing, and common interfaces.