@property装饰器如何工作?
我想了解内置函数property
如何工作。 令我困惑的是,该property
也可以用作装饰器,但只有在作为内置函数使用时才需要参数,而不能在用作装饰器时使用参数。
这个例子来自文档:
class C(object):
def __init__(self):
self._x = None
def getx(self):
return self._x
def setx(self, value):
self._x = value
def delx(self):
del self._x
x = property(getx, setx, delx, "I'm the 'x' property.")
property
的参数是getx
, setx
, delx
和一个文档字符串。
在下面的代码中, property
用作装饰器。 它的目标是x
函数,但是在上面的代码中,参数中没有对象函数的地方。
class C(object):
def __init__(self):
self._x = None
@property
def x(self):
"""I'm the 'x' property."""
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x
而且, x.setter
和x.deleter
装饰器是如何创建的? 我很困惑。
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
函数被property(foo)
替换,我们上面看到的是一个特殊的对象。 然后,当你使用@foo.setter()
,你正在做的是调用该property().setter
@foo.setter()
方法我向你展示了上面的内容,它返回属性的新副本,但是这次用setter函数替换装饰方法。
以下序列还通过使用这些装饰器方法创建了一个全开属性。
首先我们用一个getter创建一些函数和一个property
对象:
>>> 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
最后但并非最不重要的一点是, property
对象充当描述符对象,因此它具有.__get__()
, .__set__()
和.__delete__()
方法以挂钩实例属性获取,设置和删除:
>>> class Foo(object): pass
...
>>> prop.__get__(Foo(), Foo)
Get!
>>> prop.__set__(Foo(), 'bar')
Set to 'bar'!
>>> prop.__delete__(Foo())
Delete!
Descriptor Howto包含property()
类型的纯python示例实现:
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__)
文档说这只是创建只读属性的快捷方式。 所以
@property
def x(self):
return self._x
相当于
def getx(self):
return self._x
x = property(getx)
第一部分很简单:
@property
def x(self): ...
是相同的
def x(self): ...
x = property(x)
property
的简化语法。 下一步将是使用setter和deleter来扩展这个属性。 这发生在适当的方法上:
@x.setter
def x(self, value): ...
返回一个新的属性,继承旧的x
加上给定的setter的所有内容。
x.deleter
工作方式相同。