Inheritance isn’t always the solution. This article compares inheritance and composition and discusses when one is more appropriate than the other.
This text is composed from two Real Python tutorials by Isaac Rodriguez and Kyle Stratis.
Inheritance and Composition
In object-oriented programming, inheritance and composition are fundamental techniques for modeling relationships between classes and designing robust, scalable applications. They provide powerful ways to reuse code, but they do so in different ways: inheritance defines is a relationships, while composition models has a relationships. You’ll explore the meaning of these relationships and differences between them in this chapter. Understanding when and how to apply these concepts is crucial for creating maintainable and extensible codebases.
This chapter covers the principles, implementations, and trade-offs of inheritance and composition in Python. You’ll learn to identify when each approach is appropriate, how to implement them effectively, and why adhering to best practices can prevent pitfalls in your designs. By the end, you’ll have the tools to decide whether inheritance, composition, or a combination of both is the right choice for your next project.
What Are Inheritance and Composition?
Inheritance and composition are two major concepts in object-oriented programming that model the relationship between two classes. They drive the design of an application and determine how the application should evolve as new features are added or requirements change.
Both of them enable code reuse, but they do it in different ways.
What’s Inheritance?
Inheritance models what’s called an is a relationship. This means that when you have a Derived
class that inherits from a Base
class, you’ve created a relationship where Derived
is a specialized version of Base
.
Note: In an inheritance relationship:
- Classes that inherit from another are called derived classes, subclasses, or child classes.
- Classes from which other classes are derived are called base classes, super classes, or parent classes.
A derived class is said to derive from, inherit from, or extend a base class. In this lesson, the pairs of terms super class and subclass, and base class and derived class are used interchangeably.
Say you have the base class Animal
, and you derive from it to create a Horse
class. The inheritance relationship states that Horse
is an Animal
. This means that Horse
inherits the interface and implementation of Animal
, and you can use Horse
objects to replace Animal
objects in the application.
What’s Composition?
Composition is a concept that models a has a relationship. It enables creating complex types by combining objects of other types. This means that a class Composite
can contain an object of another class Component
. This relationship means that a Composite
has a Component
.
Note: Classes that contain objects of other classes are usually referred to as composites, while classes that are used to create more complex types are referred to as components.
For example, your Horse
class can be composed by another object of type Tail
. Composition allows you to express that relationship by saying Horse
has a Tail
.
Composition enables you to reuse code by adding objects to other objects, as opposed to inheriting the interface and implementation of other classes. For example, if you also have a Dog
class in your code, then both Horse
and Dog
classes can use Tail
through composition without deriving one class from the other.
Inheritance in Python
Inheritance is a key feature of every object-oriented programming language. When you write Python code using classes, you’re using inheritance even if you don’t know that you’re using it. Let’s take a look at what that means.
The Object Super Class
The easiest way to see inheritance in Python is to experiment in the Python interactive shell. You’ll start by writing the simplest class possible:
>>> class EmptyClass:
... pass
...
You declare EmptyClass
, which doesn’t do much, but it’ll illustrate the most basic inheritance concepts. Now that you declared the class, you can create an instance of the class and use the dir()
function to list its members:
>>> empty_class = EmptyClass()
>>> dir(empty_class)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']