The subject of Python properties is covered extensively here, and the Python documentation provides a pure Python implementation here. However, I am still not fully clear on the mechanics of the decorator functionality itself. More specifically, for identically named getters and setters x, how does the setter function object x (before being passed to the @x.setter decorator) not end-up rewriting the property object bound to x (thus making the decorator call meaningless)?
Consider the following example:
class C(object):
def __init__(self):
self._x = None
@property
def x(self):
"""I'm the 'x' property."""
print("getter of x called")
return self._x
@x.setter
def x(self, value):
print("setter of x called")
self._x = value
@x.deleter
def x(self):
print("deleter of x called")
del self._x
From what I understand about decorators (please correct me if I'm wrong), @property followed by def x(self): ... in the getter definition is the pure equivalent of def x(self): ... followed by x = property(x). Yet, when I try to replace all three decorators with the class constructor call syntax equivalent (while still keeping the function names identical), it stops working, like so:
class C(object):
def __init__(self):
self._x = None
def x(self):
"""I'm the 'x' property."""
print("getter of x called")
return self._x
x = property(x)
def x(self, value):
print("setter of x called")
self._x = value
x = x.setter(x)
def x(self):
print("deleter of x called")
del self._x
x = x.deleter(x)
... which results in AttributeError: 'function' object has no attribute 'setter' on line 14 (x = x.setter(x)).
This seems expected, since def x(self, value)... for the setter should overwrite x = property(x) from above.
What am I missing?
As pointed out by @kindall, the answer seems to lie in the decorator: and the fact that with it, Python does not seem to bind the raw function name to the namespace, but simply creates the raw function object, then calls the decorator function on it, and only binds the final result. This is touched upon in the answer here, answered much better here, both citing PEP318 which explains that:
... is equivalent to:
though without the intermediate creation of a variable named
func.As suggested here, this seems to be also directly evidenced by using the
dismodule to "disassemble" the code and see what is actually executing. Here is the excerpt from the output of dis command (python -m dis <filename>) ran on the code from the first example of the original question above. (This looks like the part where Python reads and interprets the class body:We can see (from what I understand) that for each decorated function definition:
LOAD_CONST (<code object x at ...>MAKE_FUNCTIONPRECALLfollowed byCALLSTORE_NAME.Finally, here is my ugly-looking but working (!) solution that tries to emulate this decorator behavior all while not using decorators and keeping the same raw function names (as initially sought in the original qeustion):
Hopefully, someone with actual knowledge of CPython, or a good source, can write or point to an actual pure Python emulation of Decorator behavior, that most closely resembles what Python does under the hood.