Inheritance
Inheritance lets a new class build on an existing one: the subclass gets all the attributes and methods of its parent and can add new ones or change how some work. It models "is a" relationships — a SavingsAccount is an Account.
Creating a subclass
class Animal: def __init__(self, name): self.name = name def speak(self): return f"{self.name} makes a sound" def describe(self): return f"I am {self.name}" class Dog(Animal): def speak(self): return f"{self.name} barks" class Cat(Animal): def speak(self): return f"{self.name} meows" for pet in [Dog("Rex"), Cat("Tom"), Animal("Generic")]: print(pet.speak(), "|", pet.describe())
Rex barks | I am Rex Tom meows | I am Tom Generic makes a sound | I am Generic
class Dog(Animal) makes Dog a subclass. It inherits __init__ and describe, and overrides speak. Code that loops over "animals" can call speak() on any of them and get the right behaviour — this is called polymorphism.
Extending the parent with super()
class Account: def __init__(self, owner, balance=0): self.owner = owner self.balance = balance def describe(self): return f"{self.owner}: ${self.balance:.2f}" class SavingsAccount(Account): def __init__(self, owner, balance=0, rate=0.02): super().__init__(owner, balance) self.rate = rate def add_interest(self): self.balance *= 1 + self.rate def describe(self): return super().describe() + f" at {self.rate:.0%}" s = SavingsAccount("Ana", 1000, rate=0.05) s.add_interest() print(s.describe())
Ana: $1050.00 at 5%
super() gives access to the parent class's version of a method. Call super().__init__(...) in a subclass initialiser so the parent can set up its own attributes, then add the extra ones.
isinstance and issubclass
class Shape: ... class Circle(Shape): ... class Square(Shape): ... c = Circle() print(isinstance(c, Circle), isinstance(c, Shape), isinstance(c, Square)) print(issubclass(Square, Shape), issubclass(Shape, Square)) print(Circle.__mro__)
True True False True False (<class '__main__.Circle'>, <class '__main__.Shape'>, <class 'object'>)
Every class ultimately inherits from object. __mro__ ("method resolution order") shows the order Python searches for attributes.
Abstract base classes
An abstract base class defines methods that subclasses must implement. Trying to create an incomplete class fails immediately:
from abc import ABC, abstractmethod class Shape(ABC): @abstractmethod def area(self): ... def describe(self): return f"{type(self).__name__} with area {self.area():.2f}" class Circle(Shape): def __init__(self, r): self.r = r def area(self): return 3.14159 * self.r ** 2 class Square(Shape): def __init__(self, side): self.side = side print(Circle(1).describe()) Square(2)
Circle with area 3.14
Traceback (most recent call last):
File "main.py", line 22, in <module>
Square(2)
~~~~~~^^^
TypeError: Can't instantiate abstract class Square without an implementation for abstract method 'area'This catches a forgotten method when the object is created, rather than much later when someone calls it.
Multiple inheritance and mixins
A class can inherit from several parents. The common, safe use is a small mixin that adds one capability:
import json class JsonMixin: def to_json(self): return json.dumps(vars(self)) class ReprMixin: def __repr__(self): fields = ", ".join(f"{k}={v!r}" for k, v in vars(self).items()) return f"{type(self).__name__}({fields})" class User(JsonMixin, ReprMixin): def __init__(self, name, email): self.name = name self.email = email u = User("Ana", "ana@x.com") print(u) print(u.to_json())
User(name='Ana', email='ana@x.com')
{"name": "Ana", "email": "ana@x.com"}Duck typing: inheritance isn't required
Python code usually cares what an object can do, not what it inherits from. Any object with the right method works:
class Duck: def speak(self): return "Quack" class Robot: def speak(self): return "Beep" for thing in [Duck(), Robot()]: print(thing.speak())
Quack Beep
"If it walks like a duck and quacks like a duck…" — that's why len() works on any object with a __len__ method.
Composition over inheritance
Inheritance is powerful but couples classes tightly. Often it's simpler for an object to have another object than to be one: a Car has an Engine rather than inheriting from it. Use inheritance for genuine "is a" relationships and shared interfaces; use composition for everything else.