Encapsulation in Python
This article provides a tutorial on encapsulation in Python, a fundamental concept in object-oriented programming.
Hello future Python wizard, welcome to Python Help!
Today we will delve into the topic of encapsulation.
Encapsulation is the practice of hiding internal details of an object from the outside world, while providing a public interface for interacting with it. This can be achieved through the use of access modifiers like public, private, and protected.
In Python, there are no true access modifiers like in other programming languages, such as Java or C++. However, we can still achieve encapsulation through the use of conventions and techniques.
Let’s start with an example. Suppose we have a class called Person that has two attributes: name and age. We want to ensure that the age attribute is never negative.
Here’s one way we could implement it:
class Person:
def __init__(self, name, age):
self.name = name
self._age = age
def get_age(self):
return self._age
def set_age(self, age):
if age < 0:
raise ValueError("Age cannot be negative")
self._age = age
Notice that we’ve prefixed the _age
attribute with an underscore, indicating that it’s intended to be private. However, this is just a convention, and other parts of our code could still access it if they really wanted to. Nonetheless, by providing public get_age() and set_age() methods, we’re effectively encapsulating the age attribute.
Let’s test it out:
person = Person("Alice", 30)
print(person.get_age()) # Output: 30
person.set_age(-5) # Raises ValueError: Age cannot be negative
As expected, trying to set a negative age raises a ValueError.
Now, let’s take a look at another example. Suppose we have a class called BankAccount that has a balance attribute, and we want to prevent other parts of our code from directly modifying it. We could achieve this by making the balance attribute private, and providing a deposit() method to add funds to the account.
Here’s what the implementation could look like:
class BankAccount:
def __init__(self, balance):
self._balance = balance
def deposit(self, amount):
if amount < 0:
raise ValueError("Deposit amount cannot be negative")
self._balance += amount
def get_balance(self):
return self._balance
Again, we’re using the underscore convention to indicate that the balance attribute is intended to be private. By only providing a deposit() method to add funds to the account, we’re encapsulating the balance attribute.
Let’s test it out:
account = BankAccount(100)
account.deposit(50)
print(account.get_balance()) # Output: 150
account._balance = 200 # We shouldn't be doing this!
print(account.get_balance()) # Output: 200
Notice that even though we can technically access the private _balance attribute, doing so goes against the principle of encapsulation.
Summary
In summary, encapsulation is a powerful technique for managing complexity and improving code maintainability. By hiding internal details of an object from the outside world, we can reduce coupling and improve modularity. While Python doesn’t have true access modifiers, we can still achieve encapsulation through the use of conventions and techniques like we’ve seen in these examples.