PythonInheritance

Inheritance

Inheritance lets one class reuse and extend the behavior of another. The class being reused is called the parent (or base/superclass), and the class that reuses it is the child (or subclass). In Python you create this relationship by writing class Child(Parent): — the parentheses after the class name list which class it inherits from.

Basic inheritance and method overriding

A subclass automatically gets every method and attribute its parent defines. It can also override a method by defining a method with the same name — when that happens, Python uses the subclass's version instead of the parent's.

Python
class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        return f"{self.name} makes a sound"


class Dog(Animal):
    def speak(self):
        return f"{self.name} says Woof!"


class Cat(Animal):
    def speak(self):
        return f"{self.name} says Meow!"


animals = [Animal("Generic critter"), Dog("Rex"), Cat("Whiskers")]
for animal in animals:
    print(animal.speak())
Generic critter makes a sound
Rex says Woof!
Whiskers says Meow!

Both Dog and Cat inherit __init__ from Animal unchanged — they never redefine it, so Dog("Rex") runs Animal.__init__. They only override speak, which is exactly the point of overriding: keep what already works, replace only the behavior that needs to differ.

Extending the parent with super()

Sometimes a subclass needs to extend the parent's behavior rather than fully replace it — for example, adding extra setup on top of whatever the parent already does. Calling super().__init__(...) runs the parent's __init__ first, and then the subclass can add its own additional state.

Python
class Employee:
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary

    def describe(self):
        return f"{self.name}: ${self.salary}/yr"


class Manager(Employee):
    def __init__(self, name, salary, team=None):
        super().__init__(name, salary)  # run Employee's setup first
        self.team = team if team is not None else []

    def describe(self):
        base = super().describe()  # reuse the parent's formatting
        return f"{base}, manages {len(self.team)} people"


alice = Employee("Alice", 65000)
bob = Manager("Bob", 95000, team=["Alice", "Carol"])

print(alice.describe())
print(bob.describe())
Alice: $65000/yr
Bob: $95000/yr, manages 2 people
super() works for regular methods too
`super()` isn't limited to `__init__`. As shown with `describe` above, you can call `super().some_method()` inside an override to run the parent's version and then add to its result, instead of duplicating the parent's logic.
Multiple levels of inheritance

Inheritance can chain across more than one level: a grandchild class can inherit from a child class, which itself inherits from a parent class. Each level can add or override behavior, and super() still walks up to the next class in that chain.

Python
class Vehicle:
    def __init__(self, brand):
        self.brand = brand

    def describe(self):
        return f"A {self.brand} vehicle"


class Car(Vehicle):
    def __init__(self, brand, doors):
        super().__init__(brand)
        self.doors = doors

    def describe(self):
        return f"{super().describe()} with {self.doors} doors"


class SportsCar(Car):
    def __init__(self, brand, doors, top_speed):
        super().__init__(brand, doors)
        self.top_speed = top_speed

    def describe(self):
        return f"{super().describe()}, top speed {self.top_speed} km/h"


car = SportsCar("Ferrari", 2, 340)
print(car.describe())
A Ferrari vehicle with 2 doors, top speed 340 km/h

Here SportsCarCarVehicle is three levels deep, and each describe override builds on the previous level's result via super() rather than rewriting it from scratch.

Checking relationships: isinstance() and issubclass()

isinstance(obj, SomeClass) checks whether obj is an instance of SomeClass or any of its subclasses. issubclass(ClassA, ClassB) checks the class relationship directly, asking whether ClassA inherits from ClassB (or is ClassB itself).

Python
rex = Dog("Rex")

print(isinstance(rex, Dog))       # rex is directly a Dog
print(isinstance(rex, Animal))    # Dog is a subclass of Animal, so True
print(isinstance(rex, Cat))       # Dog is unrelated to Cat

print(issubclass(Dog, Animal))    # Dog inherits from Animal
print(issubclass(Animal, Dog))    # the reverse is not true
print(issubclass(SportsCar, Vehicle))  # true across two levels
True
True
False
True
False
True
Note
`issubclass(SportsCar, Vehicle)` is `True` even though `SportsCar` doesn't inherit from `Vehicle` directly — it inherits from `Car`, which inherits from `Vehicle`. Both `isinstance` and `issubclass` follow the full inheritance chain, not just the immediate parent.
  • class Child(Parent): creates the inheritance relationship.

  • Overriding a method means defining one with the same name in the subclass — the subclass version wins.

  • super().method(...) calls the parent's version, useful for extending rather than fully replacing behavior.

  • isinstance() checks object-to-class membership (including subclasses); issubclass() checks class-to-class inheritance.