Python · Classes and OOP
Encapsulation
The full text of this chapter, to read or revisit. To practise, do the exercises.
▶ Do the exercisesExercise 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
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