Python · Classes and OOP
Composition
The full text of this chapter, to read or revisit. To practise, do the exercises.
▶ Do the exercisesExercise 1 — What is Composition?
Inheritance says "a WaterCodemon is a Codemon". But sometimes the relationship is "has a". A Team has Codémon. A Codémon has Moves. This is called composition — objects that contain other objects.
class Move:
def __init__(self, name, power):
self.name = name
self.power = power
class Codemon:
def __init__(self, name, moves):
self.name = name
self.moves = moves
splash = Move("Splash", 20)
fireball = Move("Fireball", 40)
myCdm = Codemon("Aquachu", [splash, fireball])
print(myCdm.moves[0].name)
print(myCdm.moves[1].power)
A `Codemon` has a list of `Move` objects. We can access the move's attributes through the Codémon: `myCdm.moves[0].name`.
Exercise 2 — Why Composition?
Composition keeps your code organized. Each class handles its own responsibility. `Move` knows about power and type. `Codemon` knows about name, level, and which moves it has.
class Move:
def __init__(self, name, power):
self.name = name
self.power = power
def __str__(self):
return f"{self.name} ({self.power} dmg)"
class Codemon:
def __init__(self, name, moves):
self.name = name
self.moves = moves
def best_move(self):
best = self.moves[0]
for move in self.moves:
if move.power > best.power:
best = move
return best
moves = [Move("Splash", 20), Move("Fireball", 40)]
myCdm = Codemon("Pyrochu", moves)
print(myCdm.best_move())
The `Codemon` class doesn't need to know how moves work internally — it just uses them. Each class has a clear job.
Exercise 3 — Accessing Nested Objects
class Move:
def __init__(self, name, power):
self.name = name
self.power = power
class Codemon:
def __init__(self, name, moves):
self.name = name
self.moves = moves
m = Move("Thunder", 35)
myCdm = Codemon("Zap", [m])
print(myCdm.moves[0].power)
Options
• Thunder
• 35
• Zap
• Error
Hint : `myCdm.moves[0]` gets the first Move object, then `.power` gets its power attribute.
Exercise 4 — Create Composition
Complete the code so the Codémon uses its move.
class Move:
def __init__(self, name, power):
self.name = name
self.power = power
class Codemon:
def __init__(self, name, move):
self.name = name
self.move = move
def attack(self):
print(f"{self.name} uses {self.____.____}!")
m = Move("Splash", 20)
myCdm = Codemon("Aquachu", m)
myCdm.attack()
Options: move name power self m
Hint : Access the move through `self.move`, then get the move's `name` attribute.
Exercise 5 — A Team Class
Let's build a `Team` class that has a list of `Codemon` objects. The team manages adding members and displaying them.
class Codemon:
def __init__(self, name, level=1):
self.name = name
self.level = level
class Team:
def __init__(self, name):
self.name = name
self.members = []
def add(self, codemon):
self.members.append(codemon)
def show(self):
print(f"Team {self.name}:")
for myCdm in self.members:
print(f" - {myCdm.name} (Lv.{myCdm.level})")
team = Team("Alpha")
team.add(Codemon("Aquachu", 15))
team.add(Codemon("Pyrochu", 20))
team.show()
Try reading the code and understanding how the `Team` class uses composition to manage its `Codemon` members.
Exercise 6 — Add to Team
Complete the `add` method to add a Codémon to the team.
class Team:
def __init__(self):
self.members = []
def add(self, codemon):
self.members.____(codemon)
def size(self):
return ____(self.members)
team = Team()
team.add("Aquachu")
team.add("Pyrochu")
print(team.size())
Options: append add len size count
Hint : To add to a list, use the list's `append` method. To get the count, use `len()`.
Exercise 7 — Team Size
class Team:
def __init__(self):
self.members = []
def add(self, name):
self.members.append(name)
team = Team()
team.add("Aquachu")
team.add("Pyrochu")
team.add("Florachu")
print(len(team.members))
Hint : Each `add()` call appends one element. Count how many times it's called.
Exercise 8 — Methods Using Composed Objects
The real power of composition shows when methods interact with the contained objects. Let's add a method to find the strongest Codémon in a team.
class Codemon:
def __init__(self, name, level=1):
self.name = name
self.level = level
class Team:
def __init__(self):
self.members = []
def add(self, codemon):
self.members.append(codemon)
def strongest(self):
best = self.members[0]
for myCdm in self.members:
if myCdm.level > best.level:
best = myCdm
return best
team = Team()
team.add(Codemon("Aquachu", 10))
team.add(Codemon("Pyrochu", 25))
team.add(Codemon("Florachu", 15))
print(f"Strongest: {team.strongest().name}")
As you can see in the code, the `strongest` method iterates through the team's members and compares their levels to find the strongest one.
Exercise 9 — Find Total Level
Complete the method to calculate the total level of all team members.
class Team:
def __init__(self):
self.members = []
def add(self, name, level):
self.members.append({"name": name, "level": level})
def total_level(self):
total = 0
for myCdm in self.____:
total += myCdm["____"]
return total
team = Team()
team.add("Aquachu", 10)
team.add("Pyrochu", 25)
print(team.total_level())
Options: members level name total myCdm
Hint : Loop through the `members` list. Each member has a `"level"` key.
Exercise 10 — Composition vs Inheritance
Match each concept with its description.
Items to match
• Inheritance
• Composition
• Team has Codémon
• Codémon has Moves
Definitions
• "is a" relationship
• "has a" relationship
• Composition example
• Composition example
Exercise 11 — Codémon Battle System
Build a Codémon with moves using composition!
1. `Move` class: `name`, `power`, `__str__` returns `"MoveName (Xdmg)"`
2. `Codemon` class: `name`, `moves` (list of Move), method `best_move()` that returns the strongest move
3. Create a Codémon with 2 moves and print its best move
Starter code
# Move class
# Codemon class
# Create and test
Back to the course