Python Inheritance

Tutorial 21 of 65 · pythondeck.com Python course

A class can inherit from one or more parents. Use super() to delegate to the next class in the method resolution order (MRO). Python supports multiple inheritance, but prefer composition or mixins when possible.

Inheritance lets a subclass reuse and extend a base class's methods and attributes. Python supports multiple inheritance and uses the MRO (method resolution order) to linearize lookup. Frameworks like Django and asyncio expose base classes you override with super() rather than copying source.

Favor shallow hierarchies; deep trees complicate reasoning and testing. Many designs use composition and protocols instead of sprawling bases, reserving inheritance for true is-a relationships such as a specialized exception type extending a base error class.

class Child(Parent): overrides methods; super() calls parent implementation.

MRO from Child.__mro__ or help(Child) shows lookup order.

Abstract bases in abc enforce interface with @abstractmethod.

Multiple inheritance: mixins should be small and document required methods.

isinstance(obj, Base) checks interface; subclassing is not the only way to satisfy protocols.

Builtins like list, dict are classes you can subclass cautiously.

super() in multiple inheritance follows MRO, not just the first parent—essential for cooperative __init__ chains in mixins.

Duck typing: if it quacks, use it—formal inheritance optional when only methods matter.

typing.Protocol supports structural typing; framework bases document super().__init__ call order.

Calling Parent.method(self) instead of super(), breaking MRO in diamonds.

Subclassing built-ins like dict without understanding update semantics.

Empty inheritance class Foo(object): clutter in Python 3 (object is implicit).

Using inheritance to share unrelated helper code—import a module instead.

Use super() consistently in cooperative multiple inheritance.

Document what subclasses must override and what __init__ expects.

Prefer composition: wrap a helper instance rather than inheriting for convenience.

Keep mixins focused—one concern per mixin class.

Print Child.__mro__ when multiple inheritance behaves unexpectedly—order is not guesswork.

Re-read the examples below with these ideas in mind; change variable names and inputs to match your own project.

The program below demonstrates single inheritance. Read the comments on each line, run the code, then change names or values to see how the output shifts.

# Example: Single inheritance
# Run in the REPL or save as a .py file and execute with python.
class Animal:
    def __init__(self, name):
        self.name = name
    def speak(self):
        return "..."

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

print(Dog("Rex").speak())

This sample walks through super() in a small, runnable script. Paste it into the REPL or save it as a .py file before you continue to the next block.

# Example: super()
# Run in the REPL or save as a .py file and execute with python.
class Base:
    def __init__(self, x):
        self.x = x

class Child(Base):
    def __init__(self, x, y):
        super().__init__(x)
        self.y = y

c = Child(1, 2)
print(c.x, c.y)

Here is a hands-on illustration of mro. Follow the inline comments first; only then execute the snippet and compare the result with what you expected.

# Example: MRO
# Run in the REPL or save as a .py file and execute with python.
class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass
print([cls.__name__ for cls in D.__mro__])

The program below demonstrates method override. Read the comments on each line, run the code, then change names or values to see how the output shifts.

# Subclasses extend superclasses and may override methods
class Animal:  # base type
    def speak(self):  # generic behavior
        return "..."  # placeholder sound

class Dog(Animal):  # inheritance
    def speak(self):  # override
        return "woof"  # dog-specific

pets = [Dog(), Animal()]  # heterogeneous list
for p in pets:  # polymorphic call
    print(p.speak())  # woof ...

This sample walks through super init in a small, runnable script. Paste it into the REPL or save it as a .py file before you continue to the next block.

# super() calls parent implementation cooperatively
class Employee:  # base employee
    def __init__(self, name):  # store name
        self.name = name  # attribute

class Manager(Employee):  # specialized employee
    def __init__(self, name, team_size):  # extra field
        super().__init__(name)  # run parent init first
        self.team_size = team_size  # child-specific attr

m = Manager("Ada", 5)  # construct manager
print(m.name, m.team_size)  # Ada 5

« Python Classes Objects All tutorials Python Iterators »