Python Composition Examples
Short, runnable programs that show python composition: has-a vs is-a in action. Read the explanation, press Run, then change the code and try your own ideas. New to the topic? Start with the Python Composition: Has-A vs Is-A lesson.
1A library that has books
The Library keeps a list of Book objects. Its methods search and update those objects instead of storing loose data of its own.
class Book:
def __init__(self, title, author):
self.title = title
self.author = author
self.available = True
class Library:
def __init__(self):
self.books = []
def add(self, book):
self.books.append(book)
def by_author(self, author):
return [book.title for book in self.books if book.author == author]
def lend(self, title):
for book in self.books:
if book.title == title and book.available:
book.available = False
return True
return False
city = Library()
city.add(Book("Emma", "Austen"))
city.add(Book("Persuasion", "Austen"))
city.add(Book("Dracula", "Stoker"))
print(city.by_author("Austen"))
print(city.lend("Emma"), city.lend("Emma"))Expected output['Emma', 'Persuasion']
True False
2Swapping a part at runtime
Alert only needs a channel with a send() method. Two unrelated classes qualify, and the channel can be replaced while the program runs.
class EmailChannel:
def send(self, text):
return f"email: {text}"
class SmsChannel:
def send(self, text):
return f"sms: {text[:20]}"
class Alert:
def __init__(self, channel):
self.channel = channel
def raise_alarm(self, reason):
return self.channel.send(f"ALERT {reason}")
alert = Alert(EmailChannel())
print(alert.raise_alarm("disk almost full"))
alert.channel = SmsChannel()
print(alert.raise_alarm("disk almost full"))Expected outputemail: ALERT disk almost full
sms: ALERT disk almost fu
3Testing with a fake part
Composition makes testing easy. Instead of a real sensor, the thermostat receives a FakeSensor that always returns the number you choose, so the result is predictable.
class Thermostat:
def __init__(self, sensor, target):
self.sensor = sensor
self.target = target
def action(self):
temp = self.sensor.read()
if temp < self.target - 1:
return "heat on"
if temp > self.target + 1:
return "cool on"
return "idle"
class FakeSensor:
def __init__(self, value):
self.value = value
def read(self):
return self.value
for reading in (16, 21, 27):
print(reading, Thermostat(FakeSensor(reading), 21).action())Expected output16 heat on
21 idle
27 cool on
4Is-a and has-a together
Employee inherits from Person because an employee is a person. It holds an Address object because an employee has an address.
class Person:
def __init__(self, name):
self.name = name
class Address:
def __init__(self, city, country):
self.city = city
self.country = country
def __str__(self):
return f"{self.city}, {self.country}"
class Employee(Person):
def __init__(self, name, address):
super().__init__(name)
self.address = address
staff = Employee("Asha", Address("Pune", "India"))
print(staff.name, "lives in", staff.address)
print(isinstance(staff, Person), isinstance(staff, Address))Expected outputAsha lives in Pune, India
True False
5Parts inside parts
A Pizza has a crust and a list of toppings. Both are the same small Ingredient class, and the pizza's price is built by asking each part for its own price.
class Ingredient:
def __init__(self, name, price):
self.name = name
self.price = price
class Pizza:
def __init__(self, crust):
self.crust = crust
self.toppings = []
def add(self, topping):
self.toppings.append(topping)
def price(self):
return self.crust.price + sum(t.price for t in self.toppings)
pizza = Pizza(Ingredient("thin crust", 8.0))
pizza.add(Ingredient("olives", 1.5))
pizza.add(Ingredient("mushrooms", 2.0))
names = [pizza.crust.name] + [t.name for t in pizza.toppings]
print(", ".join(names))
print("Price:", pizza.price())Expected outputthin crust, olives, mushrooms
Price: 11.5
Practise next
Python Composition: Has-A vs Is-A exercises → Back to the lesson
Python Inheritance examplesPython Polymorphism and Duck Typing examplesPython Multiple Inheritance and MRO examples