Encapsulation is a programming concept that refers to the bundling of data and methods that operate on that data within a single unit, or object. In Python, this concept is implemented through the use of classes.
Encapsulation is one of the four main pillars of OOP, alongside abstraction, inheritance, and polymorphism. It emphasises the bundling of data (attributes) and methods (behaviour) that operate on that data within a single unit, which is the class.
Python provides three levels of access control for class members (attributes and methods):
class Dog():
def __init__(self, name, age, breed, weight):
self.name = name
self._age = age
self.breed = breed
self.__weight = weight
dog = Dog('Bobby', 7, 'Labrador', 40)
print(dog.name)
print(dog._age)
print(dog.breed)
print(dog.__weight)
# Outputs:
# Bobby
# 7
# Labrador
# AttributeError: 'Dog' object has no attribute '__weight'
Encapsulation and abstraction often go hand in hand. Encapsulation hides the implementation details, while abstraction hides unnecessary complexities, leading to well-defined, self-contained classes.
Define class attributes and methods with appropriate access modifiers (public, protected, private).
Encapsulate attributes by making them private (using a double underscore prefix) and providing getter and setter methods to access and modify the data, respectively.
You can use property decorators to create computed attributes that provide controlled access.
Creating a Person class with private attributes like _name, _age, and methods like get_age()
and set_age()
to ensure controlled access to the attributes.
Implementing a BankAccount class with private balance (_balance
) and methods like deposit()
and withdraw()
that enforce appropriate checks and constraints.
Having a getter and setter alongside your private variables is extremely helpful way of creating a class that forces certain things to be private. If you think back to our vehicle example, you will notice that we can add our own count to each car, this time it increases on every instantiation of the object. This is something that we may not want to be accessible to everyone, because we wouldn’t want someone to maliciously change the Vehicle ID.
from abc import abstractmethod
class Vehicle:
def __init__(self, model, make, year, weight):
self.model = model
self.make = make
self.year = year
self.speed = 0
self.weight = weight
self._vehicle_ID_number = None
def move(self):
if self.year > 1970:
self.speed += 30
print("You are going 30mph!")
def get_vehicle_ID(self):
return self._vehicle_ID_number
@abstractmethod
def slow_down(self):
pass
class Car(Vehicle):
_count = 0
def __init__(self, model, make, year, weight):
super().__init__(model, make, year, weight)
Car._count += 1
self._vehicle_ID_number = Car._count
def slow_down(self, weight):
if self.speed >= 1:
self.speed -= 5
class Lorry(Vehicle):
_count = 0
def __init__(self, model, make, year, weight):
super().__init__(model, make, year, weight)
Lorry._count += 1
self._vehicle_ID_number = Lorry._count
def slow_down(self, weight):
if self.speed >= 1:
if self.weight > 100:
self.speed -= 1
elif self.weight > 75:
self.speed -= 2
elif self.weight > 50:
self.speed -= 3
else:
self.speed -= 4
car = Car("1 Series", "BMW", 2011, 24)
lorry = Lorry("Big Lorry", "Volkswagen", 2020, 74)
lorry2 = Lorry("Small Lorry", "Volkswagen", 2020, 54)
lorry2.move()
lorry2.slow_down()
print(lorry2.speed)
print(lorry2.get_vehicle_ID())