Encapsulation is a fundamental principle in object-oriented programming (OOP) that restricts access to the internal state and behavior of an object. In Python, you achieve encapsulation by defining attributes and methods that are accessible only within the class. This gives you control over how the data is changed or accessed.
Key Concepts of Encapsulation in Python
Private and Public Access Modifiers:
Public Attributes: You can access these from anywhere. In Python, any variable or method that doesn’t start with an underscore is public by default.
Private Attributes: Intended to be inaccessible from outside the class, although Python only provides a convention for indicating this (not strict enforcement).
- Prefixing an attribute or method name with a single underscore (e.g.,
_
attribute) suggests it should be treated as non-public. - Prefixing with a double underscore (e.g.,
__
attribute) causes name mangling, making it harder to access from outside the class.
Getters and Setters:
These methods control access to private attributes by providing indirect ways to read or modify the value.
Useful for adding validation or logic when data is set or retrieved.
Encapsulation Using Properties:
In Python, the @property decorator can help make getters and setters more Pythonic. Properties allow you to access attributes like regular variables, but they are managed by getters and setters behind the scenes.
Example:
class BankAccount:
def __init__(self, owner, balance=0):
self.owner = owner # Public attribute
self.__balance = balance # Private attribute using double underscore
@property
def balance(self):
"""Getter for balance."""
return self.__balance
@balance.setter
def balance(self, amount):
"""Setter for balance with validation."""
if amount < 0:
print("Invalid amount! Balance cannot be negative.")
else:
self.__balance = amount
def deposit(self, amount):
"""Method to deposit money into account."""
if amount > 0:
self.__balance += amount
print(f"Deposited ${amount}. New balance is ${self.__balance}")
else:
print("Deposit amount must be positive.")
def withdraw(self, amount):
"""Method to withdraw money from account with validation."""
if 0 < amount <= self.__balance:
self.__balance -= amount
print(f"Withdrew ${amount}. New balance is ${self.__balance}")
else:
print("Invalid withdrawal amount or insufficient funds.")
# Usage
account = BankAccount("John")
account.deposit(200)
account.withdraw(50)
print(account.balance) # Accessing balance via the getter method
account.balance = -10 # Attempting to set balance to a negative value
Explanation
Private Attribute __balance: The balance is private to prevent direct modifications from outside the class.
@property and @balance.setter: The balance property allows controlled access to the private attribute __balance. When attempting to set a negative balance, a warning is shown.
Public Methods (deposit and withdraw): These methods encapsulate logic for modifying the balance, validating input, and maintaining account integrity.
Encapsulation helps to hide internal implementation details and protect data integrity by controlling access and ensuring data consistency.