Inheritance lets subclasses reuse and customize parent class behavior, while polymorphism lets you write code that works with any object sharing a common interface — eliminating brittle type-checking and making your code extensible.
The Core Mechanism
Concept
What It Does
Syntax
Inheritance
Subclass gets parent's methods/attributes
class Child(Parent):
Method overriding
Subclass replaces a parent method
Define method with same name
super()
Calls the parent's version of a method
super().method_name()
Polymorphism
Same method call, different behavior per type
item.use(player) — each type responds differently
Abstract class
Defines contract; can't be instantiated
class Foo(ABC): with @abstractmethod
Inheritance vs. Composition
Use Inheritance When...
Use Composition When...
The relationship is "is-a"
The relationship is "has-a"
Dog is an Animal
Car has an Engine
Subclass truly substitutes for parent
Object uses another object internally
Hierarchy is 2-3 levels deep max
You need flexibility to swap components
The Polymorphism Shift
Before Polymorphism
After Polymorphism
if isinstance(x, TypeA): ...
x.do_thing()
elif isinstance(x, TypeB): ...
Each type defines its own do_thing()
Must update every if/elif for new types
New types just work
Common Bugs to Avoid
Forgetting super().__init__() — parent attributes never get set, causing AttributeError later
Using inheritance when composition is appropriate — a Stack is not a list
Deep hierarchies — keep to 2-3 levels; deeper trees are hard to debug
Breaking Liskov — subclasses should work everywhere their parent works
Quick Reference: Abstract Base Classes
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self) -> float:
... # Subclasses MUST implement this
class Circle(Shape):
def area(self) -> float:
return 3.14159 * self.radius ** 2
Design Guidelines (Section 15.9)
Favor composition over inheritance
Keep hierarchies shallow (2-3 levels)
Follow Liskov: subclasses must be substitutable for parents
Program to interfaces, not implementations
Use ABCs to enforce contracts in shared codebases
TaskFlow v1.4 Additions
DeadlineTask — has due_date, overrides is_overdue() and display()
RecurringTask — has frequency, overrides complete() to reset
ChecklistTask — has sub-items dict, tracks partial progress
What's Next
Chapter 16: OOP design patterns (Observer, dataclasses, loose coupling) and TaskFlow v1.5 with event notifications.