This is the 2nd article in a series on Object-Oriented Programming:
- Classes and Objects in Python
- Object-Oriented Programming: Encapsulation in Python
- Inheritance in Python
- Object-Oriented Programming: Polymorphism in Python
When we are driving a car in real life we don’t actually care or know how it works (unless you are an excellent mechanic).
How it calculates the current speed to show you on the panel, how the pedal connects to rest of the parts to accelerate consuming more gas in a safe way as designed by the engineers and built in a factory.
Bringing all this to our context, you don’t want someone who doesn’t actually know how your car object works to mess around with attributes they shouldn’t.
Moving the wrong parts can cause unexpected behaviors and your program will not execute the way it should, you could even have a car crash!
This is where encapsulation enters.
The right way to modify an attribute in an object, to modify its state, is to use related methods that can safely change the value of the attribute.
In languages like Java or C#, they have special ways to protect attributes that shouldn’t be touched, in Python there is no such feature, but you can use a convention to tell the other programmer they shouldn’t be using that attribute directly.
There are two types of methods and attributes: public and private.
Every public part of an object is accessible and safe to be used, the private ones can only be used inside the object itself.
A private attribute or method has double underscores __
.
To access such attributes, you have to use getters and setters.
Private methods are not to be used outside the object.
‘Car’ has 5 attributes: year
, model
, plate_number
, current_speed
, and gas
.
The ‘Car’ class contains only private attributes.
Notice how use getter and setters for every attribute except for gas
and current_speed
.
current_speed
is always initialized with zero and to increase it you have to use accelerate()
.
If you increase the speed, there is an if
that doesn’t let you accelerate more than 4 at once as a security measure.
If the first test passes then the private method __consume_gas()
is called and will check if you have enough gas, if you do, the same amount of speed increased is decreased from self.__gas
, if you don’t have enough gas, the car won’t accelerate.
Save the code below in ‘car.py’ and test some variations in the main function.
class Car:
def __init__(self, year, model, plate_number, gas):
self.__year = year
self.__model = model
self.__plate_number = plate_number
self.__gas = gas
self.__current_speed = 0
def get_year(self):
return self.__year
def set_year(self, year):
self.__year = year
def get_model(self):
return self.__model
def set_model(self, model):
self.__model = year
def set_plate_number(self, plate_number):
self.__plate_number = plate_number
def get_plate_number(self):
return self.__plate_number
def get_gas(self):
return self.__gas
def get_current_speed(self):
return self.__current_speed
def increase_gas(self, liters):
self.__gas += value
def __consume_gas(self, liters):
if(self.__gas >= liters):
self.__gas -= liters
return True
else:
return False
def accelerate(self, value):
if(value < 5 and self.__consume_gas(value)):
self.__current_speed += value
def stop(self):
self.__current_speed = 0
def details(self):
return f'{self.__model}-{self.__year}-{self.__plate_number}'
if __name__ == '__main__':
car = Car(2018, 'AI5', 'AAA0000', 6)
car.accelerate(4)
print(f'Speed: {car.get_current_speed()}-Gas:{car.get_gas()}')
car.accelerate(3)
print(f'Speed: {car.get_current_speed()}-Gas:{car.get_gas()}')
car.accelerate(1)
print(f'Speed: {car.get_current_speed()}-Gas:{car.get_gas()}')
The output for the test in the main function is:
Speed: 4-Gas:2
Speed: 4-Gas:2
Speed: 5-Gas:1
At first, the car accelerates by 4 since both if
tests passed, we are increasing the speed by a number lower than 5 and we have enough gas for it.
Later, when we try to accelerate by 3, we pass the first test, but we only have two liters of gas, so the test doesn’t pass as we remain with the same speed and gas.
Finally, we try to accelerate by 1 and this time both tests passed.
You see, we controlled how our car behaves by avoiding someone from adding too much speed at once or trying to consume more gas than it has.
Nobody can simply set self.__current_speed
to 100 because it is a private attribute and we didn’t include a setter for it.
Nobody can just consume gas if not by accelerating the car because __consume_gas(self, liters)
is private.
This is how encapsulation shines, you control exactly how the state of your object changes by avoiding collateral damages.