Key Takeaways: Inheritance and Polymorphism

One-Sentence Summary

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

  1. Forgetting super().__init__() — parent attributes never get set, causing AttributeError later
  2. Using inheritance when composition is appropriate — a Stack is not a list
  3. Deep hierarchies — keep to 2-3 levels; deeper trees are hard to debug
  4. 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)

  1. Favor composition over inheritance
  2. Keep hierarchies shallow (2-3 levels)
  3. Follow Liskov: subclasses must be substitutable for parents
  4. Program to interfaces, not implementations
  5. 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.