Encapsulation¶
Protect important state, expose only safe actions, and keep business rules close to the data they govern.
Encapsulation means an object owns its rules. Other code can request changes, but it should not be able to break the object’s internal state by accident.
Why This Matters in Business Software¶
A pricing object should not allow a negative price. A payroll object should not let random code overwrite tax totals. A bank account should not let any caller directly assign a fake balance.
Encapsulation helps you:
protect sensitive values such as balances, discounts, and permissions
centralize validation in one place instead of scattering
ifchecks everywherereduce bugs caused by direct attribute mutation
create APIs that are easier for teams to understand and trust
Learning Goals¶
By the end of this notebook, you should be able to:
explain the difference between internal state and public behavior
use methods and properties to validate updates safely
describe Python naming conventions for public, protected, and private-like attributes
connect encapsulation to inheritance and polymorphism
Core Idea¶
Encapsulation does not mean hiding everything. It means designing deliberate access.
| Design choice | Meaning in Python | Typical use |
|---|---|---|
balance | public attribute | simple data that callers may read or update directly |
_balance | internal-by-convention | internal detail that should be handled carefully |
__balance | name-mangled private-like attribute | state accessed through methods or properties |
Visual Intuition¶
Caption: External code should request changes through methods. The object decides whether the change is valid before touching its internal state.*
Worked Example: Protecting an Account Balance¶
The real protection is not the double underscore alone. The real protection is that every update goes through a method where the validation lives.
class Product:
def __init__(self, name, price):
self.name = name
self._price = 0
self.price = price
@property
def price(self):
return self._price
@price.setter
def price(self, value):
if value < 0:
raise ValueError('Price cannot be negative')
self._price = round(value, 2)
def apply_discount(self, percent):
if not 0 <= percent <= 100:
raise ValueError('Discount must be between 0 and 100')
self.price = self.price * (1 - percent / 100)
product = Product('Analytics Dashboard License', 249.99)
product.apply_discount(15)
print(product.name, product.price)Guided Practice¶
Quick MCQ¶
Which statement best captures encapsulation?
A) Put all data in global variables so every function can access it
B) Bundle data with methods and control how state changes
C) Only use inheritance when writing classes
Correct answer: B
Reflection Questions¶
Why is
balanceexposed as a property instead of a plain public variable?Which rule belongs inside the object:
discount cannot exceed 100%orthe button should be blue?What bug becomes possible if another part of the program can directly set
account.balance = -999999?
Answer Check
So writes can be validated through methods or setters.
The discount validation rule belongs inside the object because it protects business logic.
The object can enter an invalid state and break later calculations.
Exercises¶
Exercise 1¶
Build a PayrollAccount class that stores a private salary total and only allows updates through add_payment() when the amount is positive.
Exercise 2¶
Extend the Product class with a stock property that rejects negative inventory.
Exercise 3¶
Refactor one earlier class so that callers cannot directly place the object into an invalid state.
Starter Hint
Try converting one raw attribute into a property with a setter. Ask what values should be rejected and why.
Key Takeaway¶
Encapsulation means an object owns its rules. Other code may request changes, but the object decides whether those changes are valid.
This prepares the next notebook on inheritance and polymorphism: once an object has a clean public interface, subclasses can extend behavior more safely.