3 回答

TA貢獻1784條經驗 獲得超8個贊
通過Simeon Franklin的精彩演講,我終于明白了描述符和屬性的概念,下面的內容可以看作是他的講義的總結。感謝他!
要了解屬性,首先需要了解描述符,因為屬性是由描述符和python 的裝飾器語法糖實現的。別擔心,這并不難。
什么是描述符:
一個描述符是的方法實現了至少一個命名__get任何對象__(),__set __(),和__delete __()。
描述符可以分為兩類:
甲數據描述符同時實現__get __()和__set __()。
阿非數據描述符僅實現__get __()。
根據python 的 HowTo:
描述符是具有“綁定行為”的對象屬性,其屬性訪問已被描述符協議中的方法覆蓋。
那么描述符協議是什么呢?基本上來說,就是說當Python解釋器遇到像這樣的屬性訪問時obj.attr,它會按某種順序搜索來解決這個問題.attr,如果這attr是一個描述符屬性,那么這個描述符會在這個特定的順序和這個屬性訪問中占有一定的優先級將根據描述符協議轉換為對該描述符的方法調用,可能隱藏同名實例屬性或類屬性。更具體地說,如果attr是一個數據描述符,那么obj.attr會被翻譯成這個描述符的__get__方法的調用結果;如果attr不是數據描述符而是實例屬性,則匹配此實例屬性;如果attr不在上面,而且是一個非數據描述符,我們得到這個非數據描述符的 __get__ 方法的調用結果??梢栽诖颂幷业接嘘P屬性解析的完整規則。
現在讓我們談談財產。如果您查看過Python 的描述符 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__)
顯然,property 是一個數據描述符!
@property 只是使用 python 的裝飾器語法糖。
@property
def attr(self):
pass
相當于:
attr = property(attr)
所以,attr不再是我在 thie question 中發布的實例方法,而是像作者所說的那樣被裝飾器語法糖翻譯成一個類屬性。它是一個描述符對象屬性。
它如何有資格成為類屬性?
好的,我們現在解決了。
然后:
對于任何實例,所有類屬性不是都應該相同嗎?
不!
我從Simeon Franklin 的精彩演講中竊取了一個例子。
>>> 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。
添加回答
舉報