Python composition and object relationships

Build behavior by containing another object.

Python
class Engine:
    def start(self):
        return "Engine ready"

class Car:
    def __init__(self, engine):
        self.engine = engine
    def start(self):
        return self.engine.start() + "; car ready"

car = Car(Engine())
print(car.start())
Output
Engine ready; car ready

Explanation

Composition models a has-a relationship. A Car delegates starting to its Engine instead of inheriting from Engine.

Passing the dependency into the constructor makes the relationship explicit and allows a different compatible engine to be supplied later.

Explore the dry run
Guided walkthroughReady
1class Engine:
2 def start(self):
3 return "Engine ready"
4
5class Car:
6 def __init__(self, engine):
7 self.engine = engine
8 def start(self):
9 return self.engine.start() + "; car ready"
10
11car = Car(Engine())
12print(car.start())

Follow the example

Start to inspect the source, values, and output. These prepared steps explain the published sample; grouped loop steps describe multiple iterations.

View the complete dry-run table
Prepared trace of the published program. Some rows group repeated iterations.
Step / lineWhat happensState after stepOutput so far
Create an Engine and pass it into Car.car.engine = Engine instance
—
Delegate to the contained engine.car.engine = Engine instance
—
Engine supplies its result.engine result = Engine ready
—
Combine and print the result.engine result = Engine ready
Engine ready; car ready

Cost and common mistakes

O(1) calls for this fixed object relationship.

  • Car is not a kind of Engine; inheritance would misrepresent this relationship.
  • Sharing a mutable dependency means multiple owners can observe its changes.

Try it yourself

Replace Engine with a compatible ElectricMotor returning Motor ready.

Reveal answer and reasoning

Pass ElectricMotor() to Car. The result is Motor ready; car ready without changing Car.

Further reading