property()函数返回一个特殊的描述符对象:
>>> property()
<property object at 0x10ff07940>
这个对象有额外的方法:
>>> property().getter
<built-in method getter of property object at 0x10ff07998>
>>> property().setter
<built-in method setter of property object at 0x10ff07940>
>>> property().deleter
<built-in method deleter of property object at 0x10ff07998>
它们也充当装饰器。它们返回一个新的属性对象:
>>> property().getter(None)
<property object at 0x10ff079f0>
这是旧对象的副本,但替换了其中一个函数。
记住,@decorator语法只是语法糖;语法:
@property
def foo(self): return self._foo
这和
def foo(self): return self._foo
foo = property(foo)
所以函数foo被替换为属性(foo),上面我们看到的是一个特殊的对象。然后,当您使用@foo.setter()时,您所做的就是调用该属性()。setter方法,它返回属性的一个新副本,但这一次setter函数被修饰过的方法取代。
下面的序列还通过使用这些装饰器方法创建了一个完整的属性。
首先,我们创建一些函数和一个只有getter的属性对象:
>>> def getter(self): print('Get!')
...
>>> def setter(self, value): print('Set to {!r}!'.format(value))
...
>>> def deleter(self): print('Delete!')
...
>>> prop = property(getter)
>>> prop.fget is getter
True
>>> prop.fset is None
True
>>> prop.fdel is None
True
接下来我们使用.setter()方法添加一个setter:
>>> prop = prop.setter(setter)
>>> prop.fget is getter
True
>>> prop.fset is setter
True
>>> prop.fdel is None
True
最后,我们使用.deleter()方法添加了一个删除器:
>>> prop = prop.deleter(deleter)
>>> prop.fget is getter
True
>>> prop.fset is setter
True
>>> prop.fdel is deleter
True
最后,属性对象作为一个描述符对象,所以它有.__get__(), .__set__()和.__delete__()方法来挂钩到实例属性的获取,设置和删除:
>>> class Foo: pass
...
>>> prop.__get__(Foo(), Foo)
Get!
>>> prop.__set__(Foo(), 'bar')
Set to 'bar'!
>>> prop.__delete__(Foo())
Delete!
描述符Howto包含属性()类型的纯Python示例实现:
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__)