CS 112 › Lesson 4 of 10

Polymorphism & Method Overriding

Lesson 4 · OKSTEM College · Associate of Science in Computer Science

Polymorphism & Duck Typing

Polymorphism means "many forms" — the same interface works across different types. Python uses duck typing: if an object has the right methods, it works, regardless of its class.

"If it walks like a duck and quacks like a duck, it is a duck." — Python cares about behaviour, not declared type.

class Dog: def speak(self): return "Woof!" class Cat: def speak(self): return "Meow!" class Robot: def speak(self): return "Beep boop." def make_noise(thing): # accepts ANY object with .speak() print(thing.speak()) for obj in [Dog(), Cat(), Robot()]: make_noise(obj) # Woof! Meow! Beep boop.

Method Overriding with super()

Overriding replaces a parent method. Calling super().method() inside the override lets you extend rather than fully replace the behaviour.

class Logger: def log(self, msg): print(f"[LOG] {msg}") class TimestampLogger(Logger): def log(self, msg): import datetime ts = datetime.datetime.now().strftime("%H:%M:%S") super().log(f"[{ts}] {msg}") # calls Logger.log() TimestampLogger().log("Server started") # [LOG] [14:32:07] Server started

Worked Example — Shape Renderer

Define a Shape base class with area() and describe().
Subclass Circle and Rect, each overriding area().
Write a render_all(shapes) function that loops and calls describe() on each — no if/isinstance needed.
import math class Shape: def area(self): return 0 def describe(self): print(f"{self.__class__.__name__}: area = {self.area():.2f}") class Circle(Shape): def __init__(self, r): self.r = r def area(self): return math.pi * self.r ** 2 class Rect(Shape): def __init__(self, w, h): self.w=w; self.h=h def area(self): return self.w * self.h def render_all(shapes): for s in shapes: s.describe() render_all([Circle(5), Rect(4, 6), Circle(2)]) # Circle: area = 78.54 # Rect: area = 24.00 # Circle: area = 12.57

Practice Problems

1. Why is using isinstance() checks in render_all() considered bad polymorphism?

It breaks the Open/Closed principle. Every time you add a new Shape subclass, you must update render_all(). With true polymorphism, each class handles its own behaviour — render_all never changes.

2. Add a Triangle class and verify render_all() works without changes.

class Triangle(Shape): def __init__(self, b, h): self.b=b; self.h=h def area(self): return 0.5 * self.b * self.h render_all([Triangle(3, 8)]) # Triangle: area = 12.00

Knowledge Check

Duck typing means Python checks

Python is dynamically typed; declared types are optional hints.
Correct — if it has .speak(), Python calls it, no class check needed.
Python doesn't use memory addresses for method dispatch.
Everything inherits from object in Python.
Recap: duck typing = 'does this object support the interface I need?' Not 'is it a specific class?'

Polymorphism lets you

Polymorphism is about flexibility, not speed.
Correct — one make_noise() works with Dog, Cat, Robot, etc.
Polymorphism has nothing to do with constructors.
Polymorphism is specifically an OOP concept built on classes.
Recap: one interface, many implementations. render_all(shapes) doesn't care if a shape is a Circle or Rect — it just calls .area().

super().method() inside an overridden method

super() is specifically for this purpose.
Correct — lets you extend rather than fully replace parent behaviour.
super() refers to the class hierarchy, not object collections.
super() moves up the MRO — it won't loop back to the child.
Recap: super().log(msg) inside TimestampLogger.log() calls Logger.log() — extend without copy-pasting the parent code.

Which is NOT a benefit of polymorphism?

That IS a benefit — Open/Closed principle.
Each class can be tested independently.
Correct — this is NOT a benefit; polymorphism is a design concept, not a speed boost.
Generality is a core benefit.
Recap: polymorphism improves maintainability and reusability. It doesn't affect execution speed.

An isinstance() check inside a function that dispatches behaviour is a sign of

isinstance() is useful for validation at boundaries, not dispatch.
Type safety via isinstance() inside business logic is usually a smell.
Correct — if you're checking types to decide what to do, the logic belongs in the classes.
Abstract classes enforce interface contracts; they don't involve isinstance() dispatch.
Recap: tell, don't ask. Instead of asking 'what type is this?', tell the object what to do and let it respond appropriately.

← PreviousNext →