Module (80%)
Section (80%)

As those attribute name repetitions could be misleading, let's explain the naming convention:

  • the getter method is decorated with '@property'. It designates the name of the attribute to be used by the external code;
  • the setter method is decorated with '@name.setter'. The method name should be the attribute name;
  • the deleter method is decorated with '@name.deleter'. The method name should should be the attribute name.

Let's instantiate the class and perform some operations on the object's attribute:

As you can see, access to the __level attribute is handled by the designated methods by allowing the other code accessing the 'level' attribute. We can also react to operations when someone wants to break some constraints associated with the tank capacity.

The other code can make use of the 'level' attribute in a convenient way, without even knowing about the logic hidden behind it. So, whenever you'd like to control access to an attribute, you should prepare dedicated properties, because properties control only designated attributes.

It’s worth mentioning another useful and interesting feature of properties: properties are inherited, so you can call setters as if they were attributes.

Examine the code and run it to see if it follows your expectations.


Code

class TankError(Exception):
pass


class Tank:
def __init__(self, capacity):
self.capacity = capacity
self.__level = 0

@property
def level(self):
return self.__level

@level.setter
def level(self, amount):
if amount > 0:
# fueling
if amount <= self.capacity:
self.__level = amount
else:
raise TankError('Too much liquid in the tank')
elif amount < 0:
raise TankError('Not possible to set negative liquid level')

@level.deleter
def level(self):
if self.__level > 0:
print('It is good to remember to sanitize the remains from the tank!')
self.__level = None

# our_tank object has a capacity of 20 units
our_tank = Tank(20)

# our_tank's current liquid level is set to 10 units
our_tank.level = 10
print('Current liquid level:', our_tank.level)

# adding additional 3 units (setting liquid level to 13)
our_tank.level += 3
print('Current liquid level:', our_tank.level)

# let's try to set the current level to 21 units
# this should be rejected as the tank's capacity is 20 units
try:
our_tank.level = 21
except TankError as e:
print('Trying to set liquid level to 21 units, result:', e)

# similar example - let's try to add an additional 15 units
# this should be rejected as the total capacity is 20 units
try:
our_tank.level += 15
except TankError as e:
print('Trying to add an additional 15 units, result:', e)

# let's try to set the liquid level to a negative amount
# this should be rejected as it is senseless
try:
our_tank.level = -3
except TankError as e:
print('Trying to set liquid level to -3 units, result:', e)

print('Current liquid level:', our_tank.level)

del our_tank.level
{{ dockerServerErrorMsg }} ×
{{ errorMsg }} ×
{{ successMsg }} ×