Python · Classes and OOP
Composition
The full text of this chapter, to read or revisit. To practise, do the exercises.
▶ Do the exercises
Exercise 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
What is printed?
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
What is printed?
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))
Options
• 0
• 3
• 2
• Error
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