James Franco James Franco - 27 days ago 4
Python Question

Understanding property statement with class and instance variables

So I have read a lot about property keyword and I believe I have gotten the gist of it. I came across this example

class PeopleHeight:
def __init__(self, height = 150):
self.height = height

def convert_to_inches(self):
return (self.height * 0.3937)

def get_height(self):
print("Inside the getter method")
return self._height

def set_height(self, value):
if value < 0:
raise ValueError("Height cannot be negative")
print("Inside the setter method")
self._height = value

height = property(get_height, set_height) #---->CONFUSING


and the last statement which is confusing me

height = property(get_height, set_height)


I understand that the property statement has the signature

property(fget=None, fset=None, fdel=None, doc=None)


What I dont get is
get_height
and
set_height
. I understand that height is a class variable (not an instance variable) but what about
get_height
and
set_height
.I am thinking that above should have been

height = property(get_height, set_height)


but thats wrong syntax as there is no self.My question is why dont we get an error when we say:

property(get_height, set_height)


as there are no definitions of get_height or set_height set as class scope.

Answer

The statements in the class body are all executed when the class statement is executed, as if the they are all inside a function. This produces a set of names, that then are used to set the class attributes. See the class statement documentation:

The class’s suite is then executed in a new execution frame (see Naming and binding), using a newly created local namespace and the original global namespace. (Usually, the suite contains mostly function definitions.) When the class’s suite finishes execution, its execution frame is discarded but its local namespace is saved. [4] A class object is then created using the inheritance list for the base classes and the saved local namespace for the attribute dictionary.

Each of the def ... statements produce function objects, assigned to the function name, so get_height and set_height are both just functions, that then end up as attributes on the class. The same applies for the height = property(get_height, set_height) line; it accesses the names get_height and set_height, calls the property() callable with these as parameters and the result is assigned to the name height.

You may be confused how methods and the property object later on get access to the instance (the self argument in methods). Both functions and property objects are descriptor objects; any descriptor object that is accessed on an instance and is found on the class is automatically bound to the instance through this protocol. Also see the Descriptor HOWTO.

You can execute all those steps manually in the interpreter:

>>> def get_height(self):
...     print("Inside the getter method")
...     return self._height
...
>>> def set_height(self, value):
...     if value < 0:
...         raise ValueError("Height cannot be negative")
...     print("Inside the setter method")
...     self._height = value
...
>>> height = property(get_height, set_height)
>>> height
<property object at 0x105758188>
>>> height.fget
<function get_height at 0x10560ce18>
>>> height.fset
<function set_height at 0x10567e730>
>>> class FakeHeight:
...     _height = 42
...
>>> instance = FakeHeight()
>>> height.__get__(instance)  # descriptor access
Inside the getter method
42
>>> height.__set__(instance, 82)  # descriptor setting
Inside the setter method
>>> instance._height
82
Comments