Encapsulation is a crucial concept in object-oriented programming, aiming to control access to a class’s members. While this principle isn’t tied to a specific programming language, its application in Python sometimes falls short. Despite the “We’re all adults here” mindset, there’s room for improvement in our coding practices. After all, we all aspire to create top-notch software, right?
A common pattern I’ve noticed among Python developers involves exposing class members directly, as shown in the following snippet:
class File(object): def __init__(self): self.file = None def read(self): with open(self.file, 'r') as file: return file.read()
At first glance, it seems fine. You can create an instance of the class and set the file attribute as needed. However, a closer look reveals a potential issue. Consider the following sequence:
file = File() file.file = <filePath> print(file.read()) file.file = <fileThatDoesNotExist> print(file.read()) # BOOM
In this case, attempting to read a non-existent file triggers an IOError
exception. While this is expected, it doesn’t change the fact that we’re not truly leveraging encapsulation.
Let’s enhance the implementation:
import os import customExceptions class File(object): def __init__(self): self._file = None @file.setter def file(self, file): if os.path.isfile(file): raise customExceptions.CustomException('File does not exist: {}'.format(file)) self._file = file def read(self): with open(self._file, 'r') as file: return file.read()
This improved version introduces a protected member _file
and a setter method to ensure the file being set actually exists. This not only enhances encapsulation but also promotes better coding practices.
However, to truly embrace encapsulation, we should avoid direct access to the protected member, like this:
print(file._file)
Instead, we can provide a getter method:
@property def file(self): return self._file
Now, users can retrieve the file attribute in a clean and encapsulated way. If you’re working with multiple languages and frameworks, consider a more general implementation for broader consistency:
import os import customExceptions class File(object): def __init__(self): self._file = None def file(self): return self._file def setFile(self, file): if os.path.isfile(file): raise customExceptions.CustomException('File does not exist: {}'.format(file)) self._file = file def read(self): with open(self._file, 'r') as file: return file.read()
In summary, encapsulation is a must for limiting external access to a class’s members. Always start with protected or private members and implement methods for controlled access. This ensures clean, secure, and effective code.
If you found this article insightful, you may also like: