3.11 property
Note
Click here to download the full example code or to run this example in your browser via Binder
3.11 property#
Let’s say we have a for human body metabolism which has temperature as one of its parameters(attributes) and we want to have controle over this parameter in such a way that we can set the temperature from outside as well while the model itself also changes the temperature parameter when it is run.
class Model:
def __init__(self, temp):
self.temp = temp
x = Model(39)
print(x.temp)
x.temp = 35
print(x.temp)
39
35
Thus we can set the temperature from outside the class. But what if after some time we want to set certain condition on temperature attribute such as the temperature should never be above 45 degrees and below 20 degrees.
class Model:
def __init__(self, temp):
self.set_temp(temp)
def set_temp(self, x):
if x > 45:
x = 45
if x < 20:
x = 20
self.temp = x
def get_temp(self):
return self.temp
x = Model(39)
print(x.temp)
39
We can set the temperature as before but then we may violate the condition as well
x.temp = 55
print(x.temp)
55
In order to set the temperature, we have to now make use of method set_temp.
x.set_temp(55)
print(x.temp)
45
if we want to change the temperature value based upon its current value, we can do as following
print(x.get_temp())
x.set_temp(x.get_temp() * 0.9)
print(x.get_temp())
45
40.5
- we were able to decrease the temperature but there are two problems.
- First the statement
x.set_temp(x.get_temp()*0.9)
does not look so elegant, it would have been much better and clearer if we could do like
x.temp = x.temp*0.9
.
- First the statement
- There are two ways to set and get temperature which is against the
zen of python 1 which states
There should be one– and preferably only one –obvious way to do it.
Based upon above discussion we would have liked temp
to behave like attribute (x.temp)
but still be able to perform some checks on it behind the scene (which we could do by
x.temp()
) and there should be only one way to set and get its value as well.
We can solve the second problem by making temp
a private attribute i.e. by making it
__temp
, and then we have to use only setters x.set_temp()
and getters x.get_temp
.
class Model:
def __init__(self, temp):
self.set_temp(temp)
def set_temp(self, x):
if x > 45:
x = 45
if x < 20:
x = 20
self.__temp = x
def get_temp(self):
return self.__temp
x = Model(39)
print(x.get_temp())
x.set_temp(55)
print(x.get_temp())
39
45
But python provides a better solution to solve both of above two problems i.e. the @property decorator.
class Model:
def __init__(self, temp):
self.temp = temp
@property
def temp(self):
# get temperature from the model through some process
return self._temp
@temp.setter
def temp(self, x):
if x > 45:
x = 45
if x < 20:
x = 20
self._temp = x
return
x = Model(39)
print(x.temp)
x.temp = x.temp * 0.9
print(x.temp)
39
35.1
Just a side note, we don’t have provide the default value of temp upon class initiation. We can make the method to get the current temperature state of the model. In many cases our model will be saved in an external file. For simplicity, suppose, our model is saved as dictionary MODEL. We can make use of property to manipulate temp.
MODEL = {'temp': 39}
class Model:
def __init__(self):
pass
@property
def temp(self):
# get temperature from the model through some process
t = MODEL['temp']
return t
@temp.setter
def temp(self, x):
if x > 45:
x = 45
if x < 20:
x = 20
MODEL['temp'] = x
return
x = Model()
print(x.temp)
x.temp = x.temp * 0.9
print(x.temp)
39
35.1
run the model and check the temperature again
{'temp': 35.1}
What happens if we skip setter i.e. @temp.setter? This will make the attribute readonly, and any user of the class will not be able to modify its value from outside the class using instance.
39
# uncomment following line
# x.temp = x.temp * 0.9 # AttributeError
print(x.temp)
39
run the model and check the temperature again
Total running time of the script: ( 0 minutes 0.006 seconds)