
Python
Encapsulation is the practice of bundling data and methods together while hiding internal details. It protects an object's data from direct external access and provides controlled access through methods. This improves security, maintainability, and allows you to change internal implementation without affecting external code.
Encapsulation combines data and methods into a single unit (a class) and hides internal details from the outside. The object exposes a public interface (methods) while keeping internal state (attributes) private. This protects data integrity and reduces coupling between objects.
class BankAccount:
def __init__(self, owner, balance):
self._balance = balance # Private: prefix with underscore
self._owner = owner
# Public method to access private data safely
def deposit(self, amount):
if amount > 0:
self._balance += amount
return True
return False
def withdraw(self, amount):
if amount > 0 and amount <= self._balance:
self._balance -= amount
return True
return False
def get_balance(self):
return self._balance
# Use public interface
account = BankAccount("Alice", 1000)
account.deposit(500)
print(account.get_balance()) # Output: 1500
# Don't directly modify private data
# account._balance = -1000 # Bad practice, but Python allows itPrivate attributes are prefixed with an underscore (`_`). While Python doesn't enforce privacy (you can still access them), the underscore is a convention signaling "don't use this directly." This encourages using public methods instead.
class Student:
def __init__(self, name, grade):
self._name = name # Private - don't access directly
self._grade = grade # Private - don't access directly
self._gpa = 4.0 # Private - use getter
self._courses = [] # Private - use methods
# Public methods (getters)
def get_name(self):
return self._name
def get_grade(self):
return self._grade
def get_gpa(self):
return self._gpa
# Public methods (setters with validation)
def set_grade(self, grade):
if grade in ['A', 'B', 'C', 'D', 'F']:
self._grade = grade
return True
return False
def add_course(self, course):
self._courses.append(course)
def get_courses(self):
return self._courses.copy() # Return copy to prevent external modification
student = Student("Alice", "A")
print(student.get_name())
print(student.get_grade())
student.set_grade("B")
student.add_course("Math")
print(student.get_courses())Public methods form the interface that objects expose. They allow controlled access to private data and enforce business rules. Methods are the only way users should interact with object state.
class Temperature:
def __init__(self, celsius):
self._celsius = celsius
# Getters - public read access
def get_celsius(self):
return self._celsius
def get_fahrenheit(self):
return (self._celsius * 9/5) + 32
def get_kelvin(self):
return self._celsius + 273.15
# Setter - controlled write access with validation
def set_celsius(self, value):
if -273.15 <= value <= 1000: # Validation
self._celsius = value
return True
return False # Invalid temperature
def display(self):
return f"Temperature: {self._celsius}°C ({self.get_fahrenheit()}°F)"
temp = Temperature(20)
print(temp.display())
# Set through validated method
if temp.set_celsius(25):
print(temp.display())
else:
print("Invalid temperature")
# Try invalid value
if temp.set_celsius(-300):
print("Set succeeded")
else:
print("Cannot set temperature below absolute zero")Encapsulation protects data integrity by enforcing business rules. Methods ensure that data is only modified in valid ways.
class ShoppingCart:
def __init__(self):
self._items = []
self._total_price = 0
def add_item(self, name, price, quantity):
# Validation before modification
if price > 0 and quantity > 0:
self._items.append({
"name": name,
"price": price,
"quantity": quantity
})
self._total_price += price * quantity
return True
return False
def remove_item(self, name):
# Find and remove item
for item in self._items:
if item["name"] == name:
self._total_price -= item["price"] * item["quantity"]
self._items.remove(item)
return True
return False
def get_total(self):
return self._total_price
def get_items(self):
return self._items.copy()
def clear_cart(self):
self._items = []
self._total_price = 0
cart = ShoppingCart()
cart.add_item("Book", 15.99, 2)
cart.add_item("Pen", 1.50, 5)
print(f"Total: ${cart.get_total():.2f}")
cart.remove_item("Pen")
print(f"After removal: ${cart.get_total():.2f}")Python's `@property` decorator provides a Pythonic way to create getters and setters that look like attribute access but actually call methods.
class Person:
def __init__(self, first_name, last_name):
self._first_name = first_name
self._last_name = last_name
self._age = 0
# Create a property (getter)
@property
def age(self):
return self._age
# Create a setter
@age.setter
def age(self, value):
if 0 <= value <= 150:
self._age = value
else:
raise ValueError("Age must be between 0 and 150")
@property
def full_name(self):
return f"{self._first_name} {self._last_name}"
# Use like attributes but with validation
person = Person("John", "Doe")
person.age = 30 # Calls setter
print(f"{person.full_name} is {person.age}")
# Invalid assignment is prevented
try:
person.age = 200 # Raises error
except ValueError as e:
print(f"Error: {e}")class SecureBankAccount:
def __init__(self, owner, pin, balance=0):
self._owner = owner
self._pin = pin
self._balance = balance
self._transaction_history = []
def _verify_pin(self, pin):
return self._pin == pin
def deposit(self, amount, pin):
if not self._verify_pin(pin):
return "Invalid PIN"
if amount <= 0:
return "Deposit must be positive"
self._balance += amount
self._transaction_history.append(f"+${amount}")
return f"Deposited ${amount}. New balance: ${self._balance}"
def withdraw(self, amount, pin):
if not self._verify_pin(pin):
return "Invalid PIN"
if amount <= 0:
return "Withdrawal must be positive"
if amount > self._balance:
return "Insufficient funds"
self._balance -= amount
self._transaction_history.append(f"-${amount}")
return f"Withdrew ${amount}. New balance: ${self._balance}"
def get_balance(self, pin):
if not self._verify_pin(pin):
return "Invalid PIN"
return self._balance
def get_history(self, pin):
if not self._verify_pin(pin):
return "Invalid PIN"
return self._transaction_history
account = SecureBankAccount("Alice", "1234", 1000)
print(account.deposit(500, "1234"))
print(account.withdraw(200, "1234"))
print(account.get_balance("1234"))
print(account.withdraw(100, "0000")) # Wrong PINclass DatabaseConnection:
def __init__(self, host, port, database):
self._host = host
self._port = port
self._database = database
self._connected = False
self._connection = None
def connect(self):
if not self._connected:
self._connection = f"Connected to {self._host}:{self._port}/{self._database}"
self._connected = True
return "Connection established"
return "Already connected"
def disconnect(self):
if self._connected:
self._connection = None
self._connected = False
return "Connection closed"
return "Not connected"
def execute_query(self, query):
if not self._connected:
return "Error: Not connected"
return f"Executing: {query}"
def is_connected(self):
return self._connected
def get_connection_info(self):
if self._connected:
return f"Connected to {self._host}:{self._port}"
return "Not connected"
db = DatabaseConnection("localhost", 5432, "myapp")
print(db.connect())
print(db.execute_query("SELECT * FROM users"))
print(db.is_connected())
print(db.disconnect())| Concept | Remember |
|---|---|
| Encapsulation | Bundle data and methods, hide internal details |
| Private Attribute | Prefix with underscore (_attribute) |
| Public Method | Provide controlled access to private data |
| Getter | Method that returns private data safely |
| Setter | Method that validates before modifying data |
| Data Integrity | Business rules enforced through methods |
| Interface | Public methods form the contract with users |
| @property | Python decorator for property-like getters/setters |
Now let's explore magic methods (dunder methods) that give your objects special abilities.
Next: Magic Methods & Dunder →
Ready to practice? Try challenges or explore more concepts
Resources
Ojasa Mirai
Master AI-powered development skills through structured learning, real projects, and verified credentials. Whether you're upskilling your team or launching your career, we deliver the skills companies actually need.
Learn Deep • Build Real • Verify Skills • Launch Forward