Python · Classes and OOP
Encapsulation
The full text of this chapter, to read or revisit. To practise, do the exercises.
▶ Do the exercises
Exercise 1 — The Problem
Right now, anyone can change any attribute directly. What if someone sets HP to -500? Or changes the name to an empty string? That could break your game!
class Codemon: def __init__(self, name, hp=100): self.name = name self.hp = hp myCdm = Codemon("Aquachu") myCdm.hp = -500 print(f"{myCdm.name}: {myCdm.hp} HP")
Oops! A Codémon with -500 HP doesn't make sense. Encapsulation is about controlling how attributes are accessed and modified.
Exercise 2 — Private Attributes
In Python, you can signal that an attribute is private (not meant to be accessed directly) by prefixing it with an underscore `_`.
class Codemon: def __init__(self, name, hp=100): self._name = name self._hp = hp myCdm = Codemon("Aquachu") print(myCdm._name)
The `_` prefix is a convention — it tells other programmers "don't touch this directly". Python won't stop you, but you're not supposed to.
Convention, not enforcement
In Python, `_name` is a convention — it still works if accessed directly. Some languages like Java actually enforce private attributes. Python trusts the programmer!
Exercise 3 — Getters — Safe Access
Instead of accessing `_hp` directly, we create a method to read it. This is called a getter.
class Codemon: def __init__(self, name, hp=100): self._name = name self._hp = hp def get_hp(self): return self._hp def get_name(self): return self._name myCdm = Codemon("Pyrochu", 80) print(myCdm.get_name()) print(myCdm.get_hp())
Now we access data through methods, not directly. This lets us control what gets returned.
Exercise 4 — Setters — Safe Modification
A setter is a method that modifies a private attribute with validation — it checks if the new value makes sense before applying it.
class Codemon: def __init__(self, name, hp=100): self._name = name self._hp = hp def get_hp(self): return self._hp def set_hp(self, value): if value < 0: self._hp = 0 else: self._hp = value myCdm = Codemon("Aquachu") myCdm.set_hp(-500) print(myCdm.get_hp())
Now even if someone tries to set HP to -500, the setter clamps it to 0. The data is protected!
Exercise 5 — Why Encapsulate?
What is the main benefit of using getters and setters?
Options
• Makes the code longer
• Controls how data is accessed and modified
• Makes the code run faster
• Hides the code from other programmers
Hint : Think about what happens when someone tries to set HP to -500 without a setter.
Exercise 6 — Create a Getter
Complete the getter method for the Codémon's name.
class Codemon: def __init__(self, name): self._name = name def get_name(____): return self.____ myCdm = Codemon("Florachu") print(myCdm.get_name())
Options: self _name name myCdm __name
Hint : Getters take `self` and return the private attribute (with the `_` prefix).
Exercise 7 — Create a Setter
Complete the setter to prevent negative levels.
class Codemon: def __init__(self, name, level=1): self._name = name self._level = level def set_level(self, value): if value ____ 1: self._level = 1 else: self.____ = value myCdm = Codemon("Zap") myCdm.set_level(-10) print(myCdm._level)
Options: < > _level level ==
Hint : The condition checks if the value is negative. The private attribute has a `_` prefix.
Exercise 8 — Setter Validation
What is printed?
class Codemon: def __init__(self, name, hp=100): self._hp = hp def set_hp(self, value): if value > 200: self._hp = 200 elif value < 0: self._hp = 0 else: self._hp = value myCdm = Codemon("Rockachu") myCdm.set_hp(999) print(myCdm._hp)
Options
• 999
• 200
• 100
• 0
Hint : The setter has a maximum cap. What happens when the value exceeds 200?
Exercise 9 — Putting It All Together
Let's see a complete class with private attributes, getters, and setters working together.
class Codemon: def __init__(self, name, hp=100): self._name = name self._hp = hp def get_name(self): return self._name def get_hp(self): return self._hp def set_hp(self, value): self._hp = max(0, min(value, 200)) def take_damage(self, amount): self.set_hp(self._hp - amount) myCdm = Codemon("Metachu", 100) myCdm.take_damage(150) print(f"{myCdm.get_name()}: {myCdm.get_hp()} HP")
Even `take_damage` uses `set_hp` internally — so the validation always applies!
The max/min pattern
`max(0, min(value, 200))` clamps the value between 0 and 200. It's a common pattern to enforce min/max bounds.
Exercise 10 — Encapsulation Vocabulary
Match each concept with its description.
Items to match
• `_attribute`
• Getter
• Setter
• Encapsulation
• `self._hp = max(0, value)`
Definitions
• Convention for private attribute
• Method that reads a private attribute
• Method that modifies with validation
• Controlling access to data
• Prevents negative HP
Exercise 11 — Protected Codémon
Create a Codémon class with proper encapsulation: 1. Private attributes: `_name`, `_level`, `_hp` 2. Getter for each attribute 3. `set_level(value)` — must be between 1 and 100 4. `set_hp(value)` — must be between 0 and 999 5. `level_up()` that uses `set_level` to increase level by 1 6. Test by trying to set extreme values
Starter code
class Codemon: def __init__(self, name, level=1, hp=100): # Private attributes # Getters # Setters with validation # level_up method # Test it
Back to the course