Encapsulation: Control Over Your Data
The problem...
You have an Animal object. Status starts as "available". At some point in your code — or someone else's — this happens:
lassie.status = "missing"
lassie.age = -5
Python doesn't complain. The object accepts anything. A negative age. An invalid status. Data that breaks your logic silently.
With the procedural approach, you validated input at the function level. With OOP, the object itself can take responsibility for its own data.
The idea!
Encapsulation means controlling access to an object's data. Instead of allowing direct modification from outside, you define clear channels — methods — through which data can be read and changed. The object enforces its own rules.
Private attributes
In Python, a single underscore prefix signals that an attribute is intended for internal use only:
class Animal:
def __init__(self, name, age):
self.name = name
self._age = age # convention: treat as private
A double underscore makes it harder to access from outside — Python mangles the name:
class Animal:
def __init__(self, name, age):
self.name = name
self.__age = age # name-mangled — harder to access directly
lassie = Animal("Lassie", 4)
print(lassie.__age) # AttributeError
print(lassie._Animal__age) # 4 — still accessible, but clearly not intended
Python doesn't have true private attributes like some other languages. The convention is the protection. Single underscore: "please don't touch this directly." Double underscore: "I really mean it."
Getters and setters
Instead of accessing attributes directly, you define methods that read and write them — with validation built in.
class Animal:
VALID_STATUSES = ["available", "adopted"]
VALID_HEALTH = ["healthy", "ill"]
def __init__(self, name, species, age):
self.name = name
self.species = species
self._age = age
self._status = "available"
self._health = "healthy"
def get_age(self):
return self._age
def set_age(self, age):
if isinstance(age, int) and age > 0:
self._age = age
else:
print("Invalid age. Must be a positive integer.")
def get_status(self):
return self._status
def set_status(self, status):
if status in self.VALID_STATUSES:
self._status = status
else:
print(f"Invalid status. Choose from: {self.VALID_STATUSES}")
def get_health(self):
return self._health
def set_health(self, health):
if health in self.VALID_HEALTH:
self._health = health
else:
print(f"Invalid health. Choose from: {self.VALID_HEALTH}")
lassie = Animal("Lassie", "dog", 4)
lassie.set_age(5)
print(lassie.get_age()) # 5
lassie.set_age(-3) # Invalid age. Must be a positive integer.
print(lassie.get_age()) # 5 — unchanged
lassie.set_status("adopted")
print(lassie.get_status()) # adopted
lassie.set_status("missing") # Invalid status. Choose from: ['available', 'adopted']
print(lassie.get_status()) # adopted — unchanged
The object protects itself. Invalid data is rejected before it reaches the attribute.
Why this matters
In the Safe Paws project, status validation happened in the function that handled adoption. If you called a different function — or modified the dictionary directly — nothing stopped you from setting an invalid status.
With encapsulation, the validation lives inside the object. It doesn't matter how you try to change the status — the object enforces the rules every time.
Heads up!
- Single underscore
_attr— convention for "private", still accessible - Double underscore
__attr— name mangling, harder but not impossible to access - Python has no true private attributes — convention is the protection
- Getters and setters add validation — direct attribute access bypasses it
The mindset shift
Stop thinking: "I'll validate data wherever I use it."
Start thinking: "The object is responsible for its own data. It validates itself."
What you should understand now
- Encapsulation means controlling access to an object's data
- Single underscore signals "private by convention"
- Double underscore applies name mangling — harder to access directly
- Getters read private attributes, setters write them with validation
- The object enforces its own rules — regardless of where it's used