Reputation: 3039
when learning python property decorator in this link, I stumbled upon following lines of code:
class Celsius:
def __init__(self, temperature = 0):
self.temperature = temperature
def to_fahrenheit(self):
return (self.temperature * 1.8) + 32
def get_temperature(self):
print("Getting value")
return self._temperature
def set_temperature(self, value):
if value < -273:
raise ValueError("Temperature below -273 is not possible")
print("Setting value")
self._temperature = value
temperature = property(get_temperature,set_temperature)
this code basically let you modify/add restraint to a Celsius
object, without having anybody who inherited Celsius
class refactor their code.
why does it define a class variable temperature
first, rather than just let self.temperature = property(get_temperature,set_temperature)
and done?
EDIT: Due to conflict opinions in comments, I will now restore the code to original state, regardless if there is a typo, to make it easy for people read this afterwards.
Upvotes: 2
Views: 184
Reputation: 45231
The answer lies in the descriptor protocol and attribute lookup order. If you did this:
class Celsius:
def __init__(self, temperature):
self.temperature = property(get_temperature,set_temperature)
self.temperature = temperature
# etc etc
It will not behave the way you expect. At all.
t = Celsius(100)
t.temperature = -500 # NO ERROR! BROKEN!
WHY? Because you overwrote the property object with the number that was provided to the initializer. Observe:
get = lambda *args: None # dummy getter
set = lambda *args: None # dummy setter
p = property(get, set) # dummy property
A property should be an instance of Property
:
print(type(p).__name__)
# Property
But your temperature isn't a property anymore:
print(type(t.temperature).__name__)
# int
You overwrote it with this line:
self.temperature = temperature
# note that temperature is an int or float
Sprinkle some print
statements to see what is going on:
class Celsius:
def __init__(self, temperature):
self.temperature = property(get_temperature,set_temperature)
print(type(self.temperature).__name__) # Property
self.temperature = temperature
print(type(self.temperature).__name__) # no longer a Property!
# etc etc
Therefore, the property object needs to be stored at the class level so it doesn't get overwritten at the instance level. When a class level property object is accessed at the instance level, the descriptor protocol is automatically invoked (a Property
is a type of descriptor; descriptors have unusual behavior so go study them carefully).
Learn more about class level objects vs. instance level objects at this much more detailed answer.
Also note that the temperature needs to be set in the initializer using the property. Do not do this:
class Celsius:
def __init__(self, temperature):
self._temperature = temperature
Using this code you could do this without an error, which is bad: t=Celsius(-500)
. Typically the initializer should use the property to get or set the private variable just like any other method would:
self.temperature = temperature
Now an invalid initial temperature will cause an error, as expected.
Upvotes: 1
Reputation: 87
In the snippet above there are two things addressed:
1. Setting temperature during object creation using parameterized constructor.
def __init__(self, temperature = 0):
In this case Celsius with temperature 8 will be created as:
t = Celsius(8)
2. Setting and retrieving temperature using object property using
temperature = property(get_temperature,set_temperature)
In this case Celsius with temperature 8 will be created, set/retrieved as:
t = Celsius()
t.set_temperature(8)
print t.get_temperature()
Upvotes: 1
Reputation: 599550
Because methods are class properties. And self
does not exist in that scope.
Note, that code is very out of date; you should use property
as a decorator:
@property
def temperature(self):
print("Getting value")
return self._temperature
@temperature.setter
def set_temperature(self, value):
if value < -273:
raise ValueError("Temperature below -273 is not possible")
print("Setting value")
self._temperature = value
Upvotes: 0