Encapsulation
Part of the free LogicWiz Python course, Module 9: Object-oriented programming. The examples and practice run in the app.
Kabir's code ran wallet.balance = -5000 and Canteen Counter happily said he owed nothing. An object should protect its own rules, so outsiders can't push it into nonsense.
You'll learn to
- Name the four pillars of OOP and what each one means
- Use public, `_protected` and `__private` names, and explain name mangling
- Guard attributes with `@property` getters and setters
Learn
The four pillars of OOP
Every OOP interview starts here.
| Pillar | In one line | Lesson |
|---|---|---|
| Encapsulation | Keep data and its rules together; control access | 9.3 |
| Inheritance | A new class reuses and extends an old one | 9.4 |
| Polymorphism | One call, different behaviour per class | 9.5 |
| Abstraction | Show what an object does, hide how | 9.5 |
Today's pillar is encapsulation: the object, not the outside world, decides what values are allowed.
Public, _protected, __private
Python has no truly private attributes. It uses naming conventions instead:
name: public. Anyone may use it._limit(one underscore): protected by convention. "Internal, please don't touch." Python does not stop you.__balance(two underscores in front): private. Python renames it to_Wallet__balance. This is name mangling, and it makes accidental access (and clashes in subclasses) unlikely.
Name mangling in action
class Wallet:
def __init__(self, owner, balance):
self.owner = owner # public
self._limit = 2000 # protected: a polite "keep out"
self.__balance = balance # private: name-mangled
def show(self):
return f"{self.owner}: Rs {self.__balance}"
w = Wallet("Meher", 500)
print(w.show())
print(w._limit)
try:
print(w.__balance)
except AttributeError as e:
print("AttributeError:", e)
print(w._Wallet__balance) # the mangled name still works
Inside the class, self.__balance works normally. Outside, the name has become _Wallet__balance.
Name mangling is about avoiding accidents, not security. Anyone who knows the mangled name can still reach it. Real protection comes from giving the class methods that check every change, and properties make that feel natural.
@property: an attribute with a guard
In Java you would write getPrice() and setPrice(). In Python you keep the nice item.price syntax and add the checks behind it:
@propertyabovedef price(self)runs that method whenever someone readsitem.price.@price.setterabove a seconddef price(self, value)runs whenever someone assignsitem.price = ....
The real value lives in a different name, usually self._price.
A price that refuses to go negative
class Item:
def __init__(self, name, price):
self.name = name
self.price = price # this goes through the setter below
@property
def price(self):
return self._price
@price.setter
def price(self, value):
if value <= 0:
raise ValueError("price must be positive")
self._price = value
@property
def with_gst(self): # read-only and computed
return round(self._price * 1.05, 2)
samosa = Item("Samosa", 20)
print(samosa.price, samosa.with_gst)
samosa.price = 30
print(samosa.price, samosa.with_gst)
try:
samosa.price = -5
except ValueError as e:
print("Rejected:", e)
Try samosa.with_gst = 50. There is no setter, so Python refuses with an AttributeError.
Inside the setter, store the value in self._price, never self.price. Writing self.price = value calls the setter again, which calls it again… until RecursionError. Also, the getter and setter must have the same name as the property.
Interviewers love "Does Python have private variables?" Answer: no truly private ones. _x is a convention; __x is name-mangled to _ClassName__x; and @property controls how an attribute is read and written.
In one line
_x means internal, __x is name-mangled to _Class__x; @property (plus @x.setter) guards an attribute while keeping obj.x syntax.
Interview corner
What are the four pillars of OOP?
Encapsulation (bundle data with methods and control access), Abstraction (expose what an object does, hide how), Inheritance (a class reuses and extends another), and Polymorphism (the same call behaves differently for different types). Give a one-line example of each, like @property for encapsulation and len() working on lists and strings for polymorphism.
Does Python have private variables?
Not truly. A single leading underscore (_x) is a convention meaning internal. A double leading underscore (__x) triggers name mangling to _ClassName__x, which prevents accidental access and subclass clashes but can still be reached deliberately. Python relies on "we are all consenting adults".
Why use `@property` instead of `get_x()`/`set_x()` methods?
A property keeps the simple attribute syntax (obj.price = 30) while running validation or computation behind it. You can start with a plain attribute and turn it into a property later without changing any calling code. Getters and setters everywhere are considered un-Pythonic.