Inheritance in Python Explained

Learn how Python inheritance works, how child classes extend parent classes, and how method overriding and the super() function enable code reuse through class hierarchies.

7 min read

Python inheritance is the mechanism that lets a new class automatically receive all the attributes and methods of an existing class, and then add, modify, or override that inherited behavior as needed. When you define a class and place another class name in parentheses after it, you are telling Python that the new class is a specialized version of the existing one. The new class, called the child or subclass, inherits everything the existing class, called the parent or superclass, knows how to do. The child can use that inherited behavior without writing a single additional line of code, or it can override specific methods to customize them, or it can add entirely new attributes and methods that the parent never had. Inheritance is the primary mechanism for code reuse in object-oriented programming, and understanding it opens the door to building families of related classes that share common logic while specializing in different directions.

The decision to use inheritance should be guided by the relationship between the classes. If you can honestly say that a child class "is a" kind of parent class, inheritance is appropriate. A SavingsAccount is a kind of BankAccount that adds interest calculation. A PremiumCustomer is a kind of Customer that adds loyalty benefits. A Square is a kind of Shape that constrains width and height to be equal. In each case, the child class shares the parent's fundamental nature while adding or restricting behavior. If the relationship is better described as "has a," as in a Car has an Engine, then composition, where one class contains an instance of another as an attribute, is the better design choice. The articles later in this section cover composition patterns in detail, but recognizing the is-a versus has-a distinction early prevents overuse of inheritance.

If you have worked through the article on creating your first Python class, you already know the basic syntax for defining a class and writing its constructor. Inheritance extends that syntax with a single addition: the parent class name in parentheses. The article on Python constructors covers the initialization method in detail, and understanding how constructors work is essential because every child class needs to call the parent's constructor through super() to ensure proper initialization. Everything else about writing a child class, defining methods, using the self parameter, and creating objects, follows the same patterns you already know.

Basic inheritance syntax and what the child receives

The syntax for inheritance is minimal. You define a child class with the class keyword, give it a name, and put the parent class name in parentheses after the child name. The child class body can be empty, in which case it is an exact copy of the parent with a different name. More commonly, the child overrides the constructor to add its own attributes and overrides or adds methods to customize behavior.

Here is a parent class that represents a generic vehicle and a child class that represents a specific kind of vehicle with additional attributes:

pythonpython
class Vehicle:
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year
 
    def description(self):
        return f"{self.year} {self.make} {self.model}"
 
class ElectricVehicle(Vehicle):
    def __init__(self, make, model, year, battery_range):
        super().__init__(make, model, year)
        self.battery_range = battery_range

The ElectricVehicle class inherits from Vehicle. Its constructor accepts the three parameters that every vehicle needs plus an additional battery_range parameter. The call to super().init delegates the make, model, and year setup to the parent class's constructor, ensuring that those attributes are set correctly without duplicating the assignment logic. The child then adds its own battery_range attribute. The description method is inherited as-is from Vehicle; any ElectricVehicle object can call description and receive a formatted string containing the year, make, and model, even though ElectricVehicle never defined that method itself.

An ElectricVehicle object can do everything a Vehicle object can do, plus anything specific to electric vehicles. The child class did not need to reimplement description, did not need to know how the parent stores make and model, and did not need to duplicate any of the parent's logic. This is the core value of inheritance: you write shared behavior once in the parent and get it for free in every child.

Method overriding and extending parent behavior

When a child class defines a method with the same name as a method in the parent class, the child's version overrides the parent's. Any call to that method on a child instance executes the child's implementation, not the parent's. Overriding is how child classes customize behavior that the parent defined in a general way. The parent might provide a method that calculates shipping cost based on a flat rate, and the child might override it to calculate shipping based on distance and weight.

The super() function is the tool that lets an overriding method call the parent's version of the same method. This pattern, called extending rather than replacing, is common when the child wants to do everything the parent does plus something extra. The child's method calls super().method_name() to let the parent do its work, then adds its own logic after the parent call returns. Here is an example where a child class extends the parent's description method:

pythonpython
class ElectricVehicle(Vehicle):
    def __init__(self, make, model, year, battery_range):
        super().__init__(make, model, year)
        self.battery_range = battery_range
 
    def description(self):
        base = super().description()
        return f"{base} ({self.battery_range} km range)"

The child's description method calls the parent's description through super(), receives the formatted year, make, and model string, and appends the battery range information. The child did not need to know how the parent formats the description string, and if the parent's formatting logic ever changes, the child's extended version automatically reflects the update. This pattern of calling super() to build on the parent's behavior rather than replacing it entirely is one of the most common inheritance patterns in real Python code.

The super() function and cooperative multiple inheritance

The super() function is one of the most frequently used built-ins in object-oriented Python, and its behavior is subtler than it first appears. In a simple single-inheritance hierarchy, super() returns a proxy that delegates to the parent class, as shown in the previous examples. In a multiple-inheritance hierarchy where a class inherits from more than one parent, super() does not simply delegate to one parent and stop. It follows the method resolution order, a predictable sequence that determines which class's method to call next based on the full inheritance graph.

The method resolution order, often abbreviated as MRO, is the order in which Python searches for a method when it is called on an object. You can inspect the MRO of any class by calling its mro method or accessing its mro attribute. The MRO ensures that every class in the hierarchy is visited exactly once and that parent classes are always visited after their children. Understanding the MRO is essential for working with multiple inheritance, and later articles in this section cover it in detail.

For single inheritance, which covers the vast majority of inheritance use cases in everyday Python code, super() simply means "the parent class." Use super().init() in every child constructor to ensure the parent's initialization logic runs. Use super().method_name() in overriding methods when you want to extend rather than replace the parent's behavior. These two patterns cover most inheritance scenarios you will encounter.

When to use inheritance and when to avoid it

Inheritance is a powerful tool, but it is also an overused one. The temptation to create deep class hierarchies where every variation becomes a new subclass leads to code that is hard to understand, hard to test, and hard to change. A good rule of thumb is that if your inheritance hierarchy is more than three levels deep, you should reconsider whether composition or a simpler design would serve the same purpose with less complexity.

Use inheritance when child classes share the parent's interface and the parent provides meaningful default behavior that most children will use as-is or extend. The Vehicle and ElectricVehicle example demonstrates this: every vehicle has a make, model, and year, and the parent provides a sensible description method that children can use directly or extend. Avoid inheritance when the parent exists only to share a few utility methods, when children override most of the parent's methods, or when the relationship between the classes changes over time. A common refactoring pattern in mature Python codebases is to replace deep inheritance hierarchies with composition and delegation, where classes hold instances of other classes rather than inheriting from them.

The articles that follow in this section explore single inheritance, multiple inheritance, and method overriding in depth. Each builds on the foundation laid here: the syntax for declaring inheritance, the super() function for delegating to parent methods, and the is-a test for deciding when inheritance is the right design choice. Take the time to write small class hierarchies, override methods, and call super() until the patterns feel natural. Inheritance is a tool you will use in almost every non-trivial Python project, and the intuition for when it helps and when it hurts develops through practice.

Rune AI

Rune AI

Key Insights

  • Inheritance lets a child class automatically receive all attributes and methods from its parent class.
  • Method overriding lets the child replace a parent method with its own implementation while keeping the same method name.
  • The super() function delegates a method call to the parent class, commonly used to call the parent's init from the child's constructor.
  • Python supports multiple inheritance; the method resolution order determines which parent's method is called.
  • Use inheritance for 'is-a' relationships; use composition for 'has-a' relationships.
RunePowered by Rune AI

Frequently Asked Questions

What is inheritance in Python and why is it useful?

Inheritance is a mechanism that lets a new class, called a child or subclass, automatically receive the attributes and methods of an existing class, called a parent or superclass. The child class can use the inherited behavior as-is, override methods to customize them, or add entirely new attributes and methods. Inheritance reduces code duplication by letting you write shared logic once in a parent class and reuse it across multiple specialized child classes.

How do I call a parent class method from a child class in Python?

Use the super() function, which returns a proxy object that delegates method calls to the parent class. Inside a child class method, write super().method_name() to call the parent's version of that method. The most common use is calling super().__init__() in the child's constructor to ensure the parent's initialization logic runs before the child adds its own attributes.

Does Python support multiple inheritance?

Yes, Python supports multiple inheritance, where a class can inherit from more than one parent class. Python uses a method resolution order, often called the MRO, to determine which parent's method to call when multiple parents define the same method name. Multiple inheritance is powerful but can create complex dependency situations, so it should be used deliberately and with an understanding of how the MRO resolves method lookups.

Conclusion

Inheritance is the mechanism that lets you build new classes by extending existing ones rather than starting from scratch every time. The child class inherits everything the parent knows how to do and can add, override, or extend that behavior as needed. Combined with the super() function for delegating to parent methods, inheritance gives you a powerful tool for reducing duplication and building coherent class families. Use it when classes share a genuine 'is-a' relationship, and prefer composition when the relationship is better described as 'has-a.'