2012-03-28 94 views
10

請參見下面的簡單的例子:混淆在python __get__和__call__

class Celsius(object): 
    def __init__(self, value=0.0): 
     self.value = float(value) 
    def __get__(self, instance, owner): 
     return self.value 
    def __set__(self, instance, value): 
     self.value = float(value) 
    def __call__(self): 
     print('__call__ called') 

class Temperature(object): 
    celsius = Celsius() 
    def __init__(self): 
     self.celsius1 = Celsius() 


T = Temperature() 
print('T.celsius:', T.celsius) 
print('T.celsius1:', T.celsius1) 

output 
T.celsius: 0.0 
T.celsius1: <__main__.Celsius object at 0x023544F0> 

我不知道爲什麼他們有不同的輸出。 我知道T.celsius會打電話給__get__T.celsius1打電話__call__

+0

沒有什麼可遺憾,這是一個很好的問題。 – brice 2012-03-28 10:41:12

+0

所以,你自己回答了你自己的問題。 – Denis 2012-03-28 10:43:43

+1

非常好的問題。我必須檢查文檔。相當混亂的行爲,海事組織。 – codeape 2012-03-28 10:52:21

回答

2

documentation:當類包含方法(所謂的描述符類) 的一個實例出現在一個 所有者

下面的方法只適用類(描述符必須位於其所有者的類 字典中或父類之一的類字典中)。

所以描述符必須是一個類,不是一個實例的成員(即實施__get____set____delete__即對象)。

隨着followinging變化:

Temperature.celsius2 = Celsius() 

T = Temperature() 
print('T.celsius:', T.celsius) 
print('T.celsius1:', T.celsius1) 
print('T.celsius2:', T.celsius2) 

的輸出是:

T.celsius: 0.0 
T.celsius1: <__main__.Celsius object at 0x7fab8c8d0fd0> 
T.celsius2:, 0.0 

更多鏈接:

8

的差異在於這樣的事實的第一屬性是一個屬性而第二個是一個實例屬性。

作爲每the documentation,如果實現至少第一的Descriptor方法(__get____set____delete__)的對象中一個屬性被保持在被訪問時其__get__方法將被調用。 實例屬性的情況並非如此。您可以瞭解更多from the howto

對象的__call__方法僅進場當對象被調用的功能等:

>>> class Foo: 
... def __call__(self): 
...  return "Hello there!" 
... 
>>> f = Foo() 
>>> f() 
'Hello There!' 
+0

請在(和爲什麼)調用對象作爲函數時編寫。例如, – IProblemFactory 2012-03-28 12:35:29

+0

@ProblemFactory [高級裝飾器用法](http://wiki.python.org/moin/PythonDecorators)。將對象傳遞給'map','filter'或'reduce'函數以獲得高級功能。智能動態調度。代碼檢測。猴子補丁。名單繼續,繼續,... – brice 2012-03-28 12:38:58

2

T.celcius作爲類Temperature的屬性,以便它返回T的__get__方法的結果如預期的那樣。

T.celsius1是實例T的一個屬性,因此它僅返回變量本身,因爲描述符僅針對新樣式對象或類進行調用。

如果您要做T.celsius(),將使用__call__方法。

0

正如其他人所說,描述符實例旨在用作類屬性。

class Temperature(object): 
    celsius = Celsius() 
    def __init__(self): 
     self.celsius1 = Celsius() 

如果你想self.celsius1有定製行爲,覆蓋__getattr__方法:

class Temperature(object): 
    celsius = Celsius() 
    def __getattr__(self, name): 
     if name == 'celsius1': 
      return ...