Inheritance
18 min
Inheritance lets a class (the subclass) reuse and extend another
class's (the parent's) behavior, instead of rewriting it from scratch.
class Rectangle(Shape) means "a Rectangle IS-A Shape, plus whatever
extra Rectangle defines."
class Shape:
def area(self):
raise NotImplementedError # every subclass MUST override this
class Rectangle(Shape):
def __init__(self, w, h):
self.w = w
self.h = h
def area(self):
return self.w * self.h
Shape.area deliberately raises an error — it's a placeholder saying
"every real shape must define its own area," a pattern called an
abstract method.
isinstance() checks the whole inheritance chain, not just the exact class -- a Rectangle passes as both Rectangle AND Shape, since inheritance is an IS-A relationship.
When a subclass needs the parent's setup logic PLUS a bit more, calling
super().__init__(...) runs the parent's __init__ without duplicating
its code:
class Square(Rectangle):
def __init__(self, side):
super().__init__(side, side) # a square is a rectangle with w == h
Square gets area() for free — it never defines its own, so Python
looks up the inheritance chain and finds Rectangle.area, which works
perfectly since Square has self.w and self.h set (by the super()
call) exactly like any other Rectangle would.
Given `Shape` (base class) and `Rectangle(Shape)` below, implement `Rectangle.area(self)`, returning `width * height`.
Given `Square(Rectangle)` below, implement `Square.__init__(self, side)` by calling `super().__init__(side, side)` — reuse Rectangle's setup instead of duplicating it.
What does calling super().__init__(...) inside a subclass's __init__ do?
You can define a subclass that overrides a parent method, and use super() to reuse a parent's init instead of duplicating its logic.