我最终通过
Simeon Franklin's excellent presentation
以下内容可作为他讲稿的总结。谢谢他!
要理解属性,首先需要了解描述符,因为属性是由描述符和Python的decorator语法糖实现的。别担心,没那么难。
什么是描述符:
-
一
描述符
是实现至少一个名为u get_uuu()、u set_uuu()和uu delete_uuu()的方法的任何对象。
描述符可以分为两类:
-
一
数据描述符
实现uu get_uu()和uu set_uu()。
-
一
非数据描述符
仅实现uu get_uu()。
根据
python's HowTo
:
描述符是具有绑定行为的对象属性,其属性访问被描述符协议中的方法覆盖。
那么什么是描述符协议呢?基本上,它只是说当Python解释器遇到一个属性访问时
obj.attr
_¼它将以某种顺序搜索以解决此问题。
.attr
如果这个
attr
是一个描述符属性,那么这个描述符将以这个特定的顺序具有一定的优先级,并且这个属性访问将根据描述符协议被转换成对这个描述符的方法调用,可能会隐藏一个同名实例属性或类属性。更具体地说,如果
阿特尔
是一个数据描述符,那么
对象属性
将转换为此描述符的get方法的调用结果;如果
阿特尔
不是数据描述符并且是实例属性,将匹配此实例属性;如果
阿特尔
不在上面,它是一个非数据描述符,我们得到这个非数据描述符的get方法的调用结果。可以找到有关属性解析的完整规则
here
.
现在我们来谈谈财产。如果你看过
Python' descriptor HowTo
,可以找到属性的纯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__)
显然,属性是一个数据描述符!
@属性只使用python的decorator语法糖。
@property
def attr(self):
pass
相当于:
attr = property(attr)
所以,
阿特尔
不再是我在问题中发布的实例方法,而是转换为
类属性
按作者所说的装饰句法糖。它是一个描述符对象属性。
它如何有资格成为一个类属性?
好的,我们现在解决了。
然后:
对于任何实例,所有的类属性都应该是相同的,不是吗?
不!
我偷了一个例子
西蒙·富兰克林的精彩演讲
.
>>> class MyDescriptor(object):
... def __get__(self, obj, type):
... print self, obj, type
... def __set__(self, obj, val):
... print "Got %s" % val
...
>>> class MyClass(object):
... x = MyDescriptor() # Attached at class definition time!
...
>>> obj = MyClass()
>>> obj.x # a function call is hiding here
<...MyDescriptor object ...> <....MyClass object ...> <class '__main__.MyClass'>
>>>
>>> MyClass.x # and here!
<...MyDescriptor object ...> None <class '__main__.MyClass'>
>>>
>>> obj.x = 4 # and here
Got 4
注意
obj.x
以及它的输出。其输出中的第二个元素是
<....MyClass object ...>
. 这是具体的例子
obj
. 很快,因为这个属性访问被转换成了一个get方法调用,而这个get方法得到了一个特定的实例参数作为它的方法签名。
descr.__get__(self, obj, type=None)
它可以根据调用它的实例返回不同的值。
注意:我的英文解释可能不够清楚,所以我强烈建议你看一下Simeon Franklin的笔记和Python的描述符Howto。