@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的参数是getxsetxdelx和一个文档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.setterx.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是一个将方法转换为属性(也称为属性)的装饰器。