Python Polymorphism: One Interface, Many Behaviors
Table of Contents
Polymorphism is where OOP starts to feel truly powerful. You stop writing giant if type == ... trees and start trusting shared interfaces. Different objects respond to the same method call in different ways, and your calling code stays clean.
All the content from our Boot.dev courses are available for free here on the blog. This one is the “Polymorphism” chapter of Learn Object Oriented Programming in Python. If you want to try the far more immersive version of the course, do check it out!
What Is Polymorphism in Python?
Polymorphism means one interface, many implementations. In practice, classes share method names and signatures, but each class does its own thing.
class Creature:
def move(self):
return "the creature moves"
class Dragon(Creature):
def move(self):
return "the dragon flies"
class Kraken(Creature):
def move(self):
return "the kraken swims"
creatures = [Creature(), Dragon(), Kraken()]
print([creature.move() for creature in creatures])
# ['the creature moves', 'the dragon flies', 'the kraken swims']
Same call (move()), different behavior per object. That’s the whole deal.
If classes and methods still feel shaky, warm up with Python classes and objects first.
How Does Duck Typing Actually Work?
Duck typing means Python cares more about behavior than ancestry. If an object has the method your code needs, you’re good to call it.
class Griffin:
def move(self):
return "the griffin glides"
class Shark:
def move(self):
return "the shark swims"
def describe_movement(creature):
return creature.move()
print(describe_movement(Griffin())) # the griffin glides
print(describe_movement(Shark())) # the shark swims
Griffin and Shark don’t share a parent class here. It still works because they both satisfy the expected interface.
That flexibility is amazing, but it also means naming and consistency matter even more. Keep your interfaces tight and predictable, just like in Python clean code.
Why Do Shared Method Signatures Matter?
Polymorphism breaks down when signatures drift. If one class expects extra parameters and another doesn’t, caller code gets messy fast.
class Human:
def hit_by_fire(self):
return "human loses 5 health"
class Archer:
def hit_by_fire(self):
return "archer loses 10 health"
targets = [Human(), Archer()]
print([target.hit_by_fire() for target in targets])
# ['human loses 5 health', 'archer loses 10 health']
Both methods use the same shape, so callers don’t care which concrete type they’re holding.
How Is Polymorphism Used With Hitboxes?
A great real example from the course: a generic Unit checks area membership by center point, while Dragon overrides that method to use hitbox overlap.
class Unit:
def __init__(self, name, pos_x, pos_y):
self.name = name
self.pos_x = pos_x
self.pos_y = pos_y
def in_area(self, x1, y1, x2, y2):
return x1 <= self.pos_x <= x2 and y1 <= self.pos_y <= y2
class Dragon(Unit):
def __init__(self, name, pos_x, pos_y, width, height):
super().__init__(name, pos_x, pos_y)
self.width = width
self.height = height
def in_area(self, x1, y1, x2, y2):
left = self.pos_x - self.width / 2
right = self.pos_x + self.width / 2
bottom = self.pos_y - self.height / 2
top = self.pos_y + self.height / 2
return not (right < x1 or left > x2 or top < y1 or bottom > y2)
units = [Unit("Scout", 3, 3), Dragon("Nidhogg", 3, 3, 4, 2)]
print([unit.in_area(4, 4, 5, 5) for unit in units]) # [False, True]
Caller code doesn’t change. It still calls in_area(...) on each unit. That’s polymorphism doing real work.
What Is Operator Overloading in Python?
Operator overloading is built-in polymorphism with special methods. You define behavior for operators like +, ==, and < on your own classes.
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Point(self.x + other.x, self.y + other.y)
def __str__(self):
return f"({self.x}, {self.y})"
spawn = Point(4, 5)
offset = Point(2, 3)
print(spawn + offset) # (6, 8)
This works because Python maps + to __add__ under the hood.
Custom string output works the same way through __str__.
How Do Comparison Operators Make Domain Models Better?
For domain objects like cards, comparisons read much better when encoded directly into the class.
class Card:
ranks = ["2", "3", "4", "5", "6", "7", "8", "9", "10", "Jack", "Queen", "King", "Ace"]
suits = ["Clubs", "Diamonds", "Hearts", "Spades"]
def __init__(self, rank, suit):
self.rank = rank
self.suit = suit
self.rank_index = Card.ranks.index(rank)
self.suit_index = Card.suits.index(suit)
def __gt__(self, other):
if self.rank_index != other.rank_index:
return self.rank_index > other.rank_index
return self.suit_index > other.suit_index
ace_hearts = Card("Ace", "Hearts")
king_spades = Card("King", "Spades")
print(ace_hearts > king_spades) # True
Now game logic can compare cards naturally, and it stays easy to read.
When Should You Avoid Clever Polymorphism?
Polymorphism should reduce complexity, not hide it.
Skip fancy abstractions when:
- Shared interfaces are unstable
- Behavior is surprising to callers
- Overloaded operators don’t match intuition
If your + operator mutates objects in-place or your __lt__ method compares unrelated things, readers will hate you. Keep behavior unsurprising.
This is also where Learn Functional Programming in Python helps. Pure functions and explicit transforms are often clearer than over-engineered class hierarchies.
What Should You Learn After Python Polymorphism?
After this chapter, you’ve got the core OOP toolbox: classes, encapsulation, abstraction, inheritance, and polymorphism. Next step is practice in real projects where design tradeoffs are messy and constraints are real.
Finish Learn Object Oriented Programming in Python, then apply it in backend work on the Back-end Developer Path in Python and Go. That’s where these concepts stop feeling academic and start paying rent.
Frequently Asked Questions
What is polymorphism in Python?
Polymorphism means different objects can share the same method names while providing their own implementations.
Is polymorphism only for inheritance?
No. Inheritance is common, but duck typing and operator overloading are also polymorphism in Python.
What is duck typing in Python?
Duck typing means you can use an object based on what methods it has, not strictly on its class type.
What is operator overloading in Python?
Operator overloading lets classes define how operators like plus, less than, and equality behave by implementing special methods.
When should I avoid custom operator overloading?
Avoid it when the behavior is surprising or unclear. Overloads should feel natural and predictable to readers.
Related Articles
Clean Code in Python: Write Readable, Maintainable Code
Mar 20, 2026 by Lane Wagner - Boot.dev co-founder and backend engineer
If you ask ten developers for a definition of clean code, you will get twelve answers and one argument in the comments. Still, most of us agree on the practical goal: write code that other humans can understand quickly and change safely. That matters way more than writing clever one-liners that only make sense to your past self at 2 AM.
Python Classes and Objects: A Practical Beginner Guide
Mar 20, 2026 by Lane Wagner - Boot.dev co-founder and backend engineer
If classes and objects still feel slippery, you’re not crazy. Most tutorials either go too abstract or drown you in weird examples. The practical way to learn OOP is simple: understand how data and behavior stick together, then build small objects that are easy to reason about.
Python Encapsulation vs Abstraction: What Matters
Mar 20, 2026 by Lane Wagner - Boot.dev co-founder and backend engineer
If classes and objects are the “what” of OOP, encapsulation and abstraction are the “how.” They’re how you keep code understandable after the project gets big, your team grows, and six months pass. They sound similar because they’re close cousins, but they solve slightly different pains.
Python Inheritance: When to Use It and Skip It
Mar 20, 2026 by Lane Wagner - Boot.dev co-founder and backend engineer
Inheritance is one of those OOP tools that feels magical on day one and dangerous on day thirty. Used well, it removes duplication and keeps models clean. Used badly, it creates class trees no one wants to touch.