Day 27 – Encapsulation & Property
Project: Build a “Bank Account System” that demonstrates encapsulation, private attributes, and the use of Python’s @property decorator.
01. Learning Goal
By the end of this lesson, you will be able to:
- Understand encapsulation and why it matters in OOP
- Use private attributes (
_and__) to hide internal data - Access private variables through getter/setter methods
- Simplify code with the
@propertydecorator - Create read-only properties for secure, immutable data
02. Problem Scenario
You are building a bank account system where each user’s balance must be private and protected.
Users should not modify balances directly — only through controlled methods that validate input.
Encapsulation ensures that sensitive data remains safe from misuse.
03. Step 1 – Encapsulation Basics
Encapsulation hides internal details of an object and exposes only the necessary interface.
In Python, attributes starting with _ or __ are treated as private by convention.
class Person:
def __init__(self, name, age):
self.name = name # Public
self.__age = age # Private
def get_age(self):
return self.__age # Access via method
p = Person("Sabin", 30)
print(p.name) # Sabin
# print(p.__age) # ❌ AttributeError
print(p.get_age()) # 30
04. Step 2 – Getter and Setter Methods
Use getter and setter methods to safely access and modify private attributes.
class Account:
def __init__(self, balance):
self.__balance = balance
def get_balance(self):
return self.__balance
def set_balance(self, amount):
if amount >= 0:
self.__balance = amount
else:
print("Invalid balance")
acc = Account(100)
print(acc.get_balance()) # 100
acc.set_balance(200)
print(acc.get_balance()) # 200
05. Step 3 – Using @property
Python’s @property decorator allows you to create getter/setter functionality
while accessing attributes as if they were public.
class Account:
def __init__(self, balance):
self.__balance = balance
@property
def balance(self): # Getter
return self.__balance
@balance.setter
def balance(self, amount): # Setter
if amount >= 0:
self.__balance = amount
else:
print("Invalid balance")
acc = Account(100)
print(acc.balance) # 100
acc.balance = 300
print(acc.balance) # 300
Now you can access and modify balance naturally without breaking encapsulation.
06. Step 4 – Read-Only Property
Define only a getter (no setter) to make a property read-only.
class Circle:
def __init__(self, r):
self.__r = r
@property
def area(self):
return 3.14 * self.__r * self.__r
c = Circle(5)
print(c.area) # 78.5
# c.area = 100 # ❌ AttributeError: can't set attribute
07. Step 5 – Practice Examples
Example 1: Temperature Validation
class Temperature:
def __init__(self, celsius):
self.__celsius = celsius
@property
def celsius(self):
return self.__celsius
@celsius.setter
def celsius(self, value):
if value >= -273: # Absolute zero check
self.__celsius = value
else:
print("Invalid temperature")
t = Temperature(25)
print(t.celsius) # 25
t.celsius = -300 # Invalid temperature
Example 2: Student with Read-only Grade
class Student:
def __init__(self, name, grade):
self.name = name
self.__grade = grade
@property
def grade(self): # Read-only property
return self.__grade
s = Student("Anna", "A")
print(s.name, s.grade) # Anna A
# s.grade = "B" # ❌ AttributeError: can't set attribute
08. Mini Project – Secure Bank Account
Build a simple bank account system using encapsulation and properties.
class BankAccount:
def __init__(self, owner, balance):
self.owner = owner
self.__balance = balance
@property
def balance(self):
return self.__balance
@balance.setter
def balance(self, amount):
if amount >= 0:
self.__balance = amount
else:
print("Invalid amount — balance cannot be negative.")
def deposit(self, amount):
if amount > 0:
self.__balance += amount
print(f"Deposited {amount}. New balance: {self.__balance}")
else:
print("Deposit must be positive.")
def withdraw(self, amount):
if 0 < amount <= self.__balance:
self.__balance -= amount
print(f"Withdrew {amount}. New balance: {self.__balance}")
else:
print("Invalid withdrawal amount.")
acc = BankAccount("Sabin", 1000)
acc.deposit(500)
acc.withdraw(200)
print("Final balance:", acc.balance)
Output:
Deposited 500. New balance: 1500
Withdrew 200. New balance: 1300
Final balance: 1300
09. Reflection
You have learned how to:
- Use encapsulation to protect data integrity
- Apply getter/setter methods for controlled access
- Simplify with the
@propertydecorator - Create read-only properties for immutable data
- Apply these concepts in real-world systems (like banking)
Next → Day 28 – Inheritance + Polymorphism
Learn how to combine inheritance with polymorphism to design flexible, extensible class hierarchies.
Top comments (0)