2013-10-06 105 views
1
class Cls(): 

    def __init__(self, start): 
     self.value = start 


    class Desc(): 
     def __get__(self, instance ,owner): 
      print("In Descriptor's __get__method") 
      return self.value 
     def __set__(self, instance, start): 
      print("In Descriptor's __set__ method") 
      self.value = start 

    value = Desc() 

X = Cls('Hello') 
X.value = "Hi" 

描述符的上述實現對我來說是模糊的。 X.value和Cls.value引用同一個對象,並且是str類。但Cls .__ dict __ ['value']是描述符對象。有兩種類型分配給名稱「值」。Python描述符的特定實現

有人可以解釋這一點嗎?這個特定實現的邏輯是什麼?爲什麼Cls.value或X.value不是描述符對象。我正在使用python 3.3

+1

您瞭解描述符協議的工作原理嗎?因爲你所描述的聽起來像它的設計工作。描述符協議在執行'obj.value'時被激活。當您執行'obj .__ dict __ ['value']'時,它不會被激活。 – BrenBarn

+0

這意味着有'值'是指在Cls命名空間中的字符串對象「hello」。然後還有另一個'值'指向Cls命名空間中的描述符對象 – user634615

+0

您有誤解。 '__set__' /'__get__'方法中的'self'引用描述符對象本身,而不是'Cls'實例。看到我編輯的答案。 – BrenBarn

回答

4

通過使用名稱value有兩件事情令人困惑:一個是Cls的屬性,其值是一個描述符對象。另一個是該描述符對象的一個​​屬性,這就是其值爲字符串的屬性。

要記住的關鍵是,只有一個描述符對象,跨類的所有實例共享。當在__set__方法中使用self.value = start時,self引用描述符對象,因此您要在描述符對象本身而不是Cls實例上設置「value」屬性。 (如果您將其更改爲instance.value = start相反,你會得到一個遞歸的錯誤,因爲這將再次嘗試撥打__set__。)

你會看到正在發生的事情,如果你創建類的多個實例:

>>> x = Cls("oops") 
In Descriptor's __set__ method 
>>> y = Cls("dang") 
In Descriptor's __set__ method 
>>> x.value 
In Descriptor's __get__method 
'dang' 
>>> Cls.__dict__['value'].value 
'dang' 

請注意,創建y已更改x.value。這是因爲只有一個描述符對象,並且它只有一個「值」屬性,因此該值將在Cls的所有實例中共享。

目前還不清楚你在這裏試圖達到什麼目標,所以很難說如何「修復」這一點。一些基本原則是:

  1. ,除非你想存儲類級別的信息不self__get____set__使用。要存儲特定於實例的數據,您需要使用instance
  2. 即使您完成上述操作,也不要使用描述符屬性本身的相同名稱和存儲其數據的隱藏屬性,否則您將踩在自己的腳趾上。
  3. 除非你想做一些非常奇特的事情,否則你可能只需要使用property而不是寫你自己的描述符。如果你「固定」了上面所寫的描述符,它仍然沒用,因爲它不會做任何property尚未做的事情。
+0

你是對的價值創造混亂的用法。進一步將值存儲在描述符對象名稱空間中會造成更多混淆。我知道了 – user634615

+0

我還有一個擔心是(可能是它的設計問題)爲什麼類型(Cls.value)是class str。它不應該是描述符對象 – user634615

+0

鍵入'Cls.value'將計算爲'Cls。__dict __ ['value'] .__ get __(None,Cls)'。如果它返回一個字符串,那麼類型將是'str'。它永遠不會是描述符(除非你的'__get__'自己返回)。 – abarnert