@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
和一个文档string。
在下面的代码中, 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
而且,如何创buildx.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>
这是旧对象的副本,但是其中一个function被replace了。
请记住, @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函数replace装饰的方法。
以下序列还通过使用这些装饰器方法创build了一个完整的属性。
首先我们用一个getter创build一些函数和一个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()
types的纯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__)
文档说这只是创build只读属性的快捷方式。 所以
@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)
- 反过来,这也是用getter创build一个
property
的简化语法。
下一步将是用一个setter和一个deleter来扩展这个属性。 这发生在适当的方法:
@x.setter def x(self, value): ...
返回一个新的属性,它inheritance旧的x
加上给定的setter。
x.deleter
工作方式相同。
这是一个如何实现@property
的最简单的例子:
class Thing: def __init__(self, my_word): self._word = my_word @property def word(self): return self._word >>> print( Thing('ok').word ) 'ok'
否则, word
仍然是一种方法,而不是属性。
class Thing: def __init__(self, my_word): self._word = my_word def word(self): return self._word >>> print( Thing('ok').word() ) 'ok'
以下内容:
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
是相同的:
class C(object): def __init__(self): self._x = None def _x_get(self): return self._x def _x_set(self, value): self._x = value def _x_del(self): del self._x x = property(_x_get, _x_set, _x_del, "I'm the 'x' property.")
是相同的:
class C(object): def __init__(self): self._x = None def _x_get(self): return self._x def _x_set(self, value): self._x = value def _x_del(self): del self._x x = property(_x_get, doc="I'm the 'x' property.") x = x.setter(_x_set) x = x.deleter(_x_del)
是相同的:
class C(object): def __init__(self): self._x = None def _x_get(self): return self._x x = property(_x_get, doc="I'm the 'x' property.") def _x_set(self, value): self._x = value x = x.setter(_x_set) def _x_del(self): del self._x x = x.deleter(_x_del)
这是一样的:
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
一个属性可以用两种方式来声明。
- 为属性创buildgetter,setter方法,然后将这些作为parameter passing给属性函数
- 使用@property装饰器。
你可以看看我已经写了关于python属性的几个例子。
@property是一个将方法转换为属性(也称为属性)的装饰器。