I have this
#!/usr/bin/env python
import math
class myclass(object):
def __init__(self,radius):
self.radius = radius
@property
def area(self):
print myclass.area, type(myclass.area)
return math.pi * ( self.radius ** 2)
@area.setter
def area(self,value):
print myclass.area, type(myclass.area)
pass
@area.deleter
def area(self):
print myclass.area, type(myclass.area)
del myclass.area
if __name__ == '__main__':
c = myclass(5.4)
c.area
c.area = 65
del c.area
This gives:
$ ./propertytest.py
<property object at 0x7ff0426ac0a8> <type 'property'>
<property object at 0x7ff0426ac0a8> <type 'property'>
<property object at 0x7ff0426ac0a8> <type 'property'>
Question:
Look at the way property object area
has been accessed: c.area
. area
appears on the right side of the dot operator. Which special method is used by the property object to bind the class instance object with the right instance method and compute the result? How do properties work?
Properties are descriptors. A descriptor is an instance of a class with __get__
, __set__
and/or __delete__
method. Whenever python does getattr
, setattr
and delattr
, if the instance does not have said attribute, and in the class there is a attribute with that name, that has the matching magic method, the magic method is called instead of the reading/writing/deleting the attribute.
The Python documentation tells more on descriptors and also has this pure python emulation of builtin property
type:
class Property(object):
"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__)
To make what is happening above a bit more explicit, let's get rid of the decorator syntax sugar:
import math
class myclass(object):
def __init__(self,radius):
self.radius = radius
def getter(self):
print myclass.area, type(myclass.area)
return math.pi * ( self.radius ** 2)
# create a property object with "getter" as the getter, assigned to "area"
area = property(getter)
# area.__get__ is now a method that wraps "getter"
def setter(self,value):
print myclass.area, type(myclass.area)
pass
# create a copy of the "area" object with "setter" as a setter, assign back to "area"
area = area.setter(setter)
# area.__set__ is now a method that wraps "setter"
def deleter(self):
print myclass.area, type(myclass.area)
del myclass.area
# create a copy of the "area" object with "deleter" as a deleter, assign back to "area"
area = area.deleter(deleter)
# area.__delete__ is now a method that wraps "deleter"
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.