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:
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_rangeThe 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:
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
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.
Frequently Asked Questions
What is inheritance in Python and why is it useful?
How do I call a parent class method from a child class in Python?
Does Python support multiple inheritance?
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.'
More in this topic
Create Custom Iterators in Python
Learn how to build your own iterators in Python by implementing the __iter__() and __next__() methods, with practical examples and reusable patterns.
Python Iterator Protocol Explained
Learn how Python's iterator protocol works with the __iter__() and __next__() methods, and understand the contract every iterator must follow.
Python Iterables and Iterators Explained
Learn what iterables and iterators are in Python, how they power for loops under the hood, and why understanding them unlocks cleaner data processing patterns.