Reputation: 339
In the below code that emulates property creation code:
class Property:
"Emulate PyProperty_Type() in Objects/descrobject.c"
def __init__(self, fget=None, fset=None, fdel=None, doc=None):
self.fget = fget
self.fset = fset
self.fdel = fdel
if doc is None and fget is not None:
doc = fget.__doc__
self.__doc__ = doc
def __get__(self, obj, objtype=None):
if obj is None:
return self
if self.fget is None:
raise AttributeError("unreadable attribute")
return self.fget(obj)
def __set__(self, obj, value):
if self.fset is None:
raise AttributeError("can't set attribute")
self.fset(obj, value)
def __delete__(self, obj):
if self.fdel is None:
raise AttributeError("can't delete attribute")
self.fdel(obj)
def getter(self, fget):
return type(self)(fget, self.fset, self.fdel, self.__doc__)
def setter(self, fset):
return type(self)(self.fget, fset, self.fdel, self.__doc__)
def deleter(self, fdel):
return type(self)(self.fget, self.fset, fdel, self.__doc__)
in __get__
, self
is the instance of the property class?
Also, what does obj
refer to? Why is obj
passed to self.fget
?
When I test it I get the following error:
class A:
func = Property(fget=func)
a = A()
a.func
TypeError: 'Property' object is not callable
How does it work then if it's not callable? I was under the impression that when defining a decorator as an instance you need to make sure it implements the __call__
method?
What am I missing?
Upvotes: 1
Views: 46
Reputation: 532238
self
is the instance of Property
itself. Its main purpose is to simply hold references to the getter, setter, and/or deleter so that you can call them when the property is accessed. When those functions are called, they get a reference to the object from which the property was accessed, so in a.func
, self
is the instance of Property
and obj
is a
.
The Property
instance itself doesn't need to be callable, because it's not what gets called. Property.__get__
only returns self if you access it from the class, not an instance of the class. The built-in property
works the same way.
>>> class A:
... func = property(lambda self: 3)
...
>>> A.func
<property object at 0x10a33bb88>
>>> A().func
3
func
is a class attribute whose value is an instance of property
. The result of accessing the attribute differs depending on whether you look it up on the class itself or an instance of the class.
When accessed via the class, you get back the property
itself.
A.func == A.__dict__['func'].__get__(None, A)
== A.__dict__['func']
When accessed via an instance, you get back the result of calling the original function passed to property
.
a = A()
a.func == A.__dict__['func'].__get__(a, A)
== A.__dict__['func'].fget(a)
== (lambda self: 3)(a)
== 3
Upvotes: 1