⚙️
Intermediate
Inheritance · super() · Polymorphism · __str__ · __eq__ · __len__
Week 8 — OOP Part 2: Inheritance & Dunder Methods
Extend classes with inheritance, call parent methods with super(), implement polymorphism, and customize object behavior with dunder (magic) methods.
inheritancesuperpolymorphismdunderABC
Duration
⏱ 3 hours
Level
📊 Intermediate
Prerequisite
🎯 Week 7
OUTCOME
Build a shape hierarchy with area/perimeter polymorphism and rich comparison
What you'll learn
- 1Create a child class that inherits from a parent
- 2Call parent __init__ with super()
- 3Override methods for polymorphism
- 4Define abstract base classes with ABC
- 5Implement __str__, __repr__, __eq__, __lt__, __len__, __add__
1. Inheritance
python
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
raise NotImplementedError
class Dog(Animal):
def speak(self):
return f"{self.name}: Woof!"
class Cat(Animal):
def speak(self):
return f"{self.name}: Meow!"
for a in [Dog("Fido"), Cat("Whiskers")]:
print(a.speak())2. Abstract Base Classes
python
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self) -> float: ...
@abstractmethod
def perimeter(self) -> float: ...
def __str__(self):
return f"{self.__class__.__name__}: area={self.area():.2f}"
class Circle(Shape):
def __init__(self, r): self.r = r
def area(self): return 3.14159 * self.r ** 2
def perimeter(self): return 2 * 3.14159 * self.r
class Rectangle(Shape):
def __init__(self, w, h): self.w, self.h = w, h
def area(self): return self.w * self.h
def perimeter(self): return 2 * (self.w + self.h)
shapes = [Circle(5), Rectangle(3, 4)]
for s in shapes: print(s)3. Dunder Methods
python
class Vector:
def __init__(self, x, y):
self.x, self.y = x, y
def __repr__(self):
return f"Vector({self.x}, {self.y})"
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __eq__(self, other):
return self.x == other.x and self.y == other.y
def __abs__(self):
return (self.x**2 + self.y**2)**0.5
v1 = Vector(1, 2)
v2 = Vector(3, 4)
print(v1 + v2) # Vector(4, 6)
print(abs(v2)) # 5.04. Common Mistakes
- Forgetting to call super().__init__() in the child — parent attributes won't be initialized.
- Overriding __eq__ without __hash__ makes the object unhashable (can't be used in sets/dicts).
- Multiple inheritance with diamond problem — Python's MRO (C3 linearization) resolves this, but be cautious.
💻 Examples
Run these examples and check the output yourself.
01_shapes.py— Shape hierarchy with polymorphic area/perimeter
CODE
import math
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self): ...
@abstractmethod
def perimeter(self): ...
def __str__(self):
return f"{type(self).__name__}(area={self.area():.2f}, perim={self.perimeter():.2f})"
class Circle(Shape):
def __init__(self, r): self.r = r
def area(self): return math.pi * self.r**2
def perimeter(self): return 2 * math.pi * self.r
class Rect(Shape):
def __init__(self, w, h): self.w, self.h = w, h
def area(self): return self.w * self.h
def perimeter(self): return 2*(self.w+self.h)
for s in [Circle(5), Rect(3, 4), Circle(1)]:
print(s)
▶ Output
Circle(area=78.54, perim=31.42)
Rect(area=12.00, perim=14.00)
Circle(area=3.14, perim=6.28)📝 Exercises
Try them yourself first, then open the solution to compare.
Exercise 1
Fraction class
Goal: Implement a Fraction class with arithmetic operators.
Requirements
- __init__(numerator, denominator)
- Reduce to lowest terms using gcd
- __add__, __sub__, __mul__, __truediv__
- __str__ → '3/4', __repr__ → Fraction(3, 4)
- __eq__ for comparison
▶Toggle solution
SOLUTION
from math import gcd
class Fraction:
def __init__(self, n, d):
if d == 0: raise ZeroDivisionError
g = gcd(abs(n), abs(d))
self.n, self.d = n//g * (1 if d>0 else -1), abs(d)//g
def __repr__(self): return f"Fraction({self.n}, {self.d})"
def __str__(self): return f"{self.n}/{self.d}"
def __add__(self, o): return Fraction(self.n*o.d + o.n*self.d, self.d*o.d)
def __sub__(self, o): return Fraction(self.n*o.d - o.n*self.d, self.d*o.d)
def __mul__(self, o): return Fraction(self.n*o.n, self.d*o.d)
def __truediv__(self, o): return Fraction(self.n*o.d, self.d*o.n)
def __eq__(self, o): return self.n == o.n and self.d == o.d
a, b = Fraction(1,2), Fraction(1,3)
print(a+b, a-b, a*b, a/b)
▶ Output
5/6 1/6 1/6 3/2Example code / lecture materials
All lecture materials and example code are openly available on GitHub.
View on GitHub ↗