2012-03-28 12 views
10

Siehe einfaches Beispiel unten:Verwirrung über __get__ und __call__ in Python

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> 

Ich frage mich, warum sie unterschiedlichen Ausgang haben. Ich weiß, T.celsius wird rufen Sie die und T.celsius1 rufen Sie die __call__.

+0

Nichts über leid zu sein, das ist eine gute Frage. – brice

+0

Und so hast du deine eigene Frage beantwortet. – Denis

+1

Sehr gute Frage. Ich musste die Dokumentation überprüfen. Ziemlich verwirrendes Verhalten, IMO. – codeape

Antwort

0

Wenn Sie das versuchen, werden Sie dasselbe Ergebnis sehen.

print('T.celsius:', T.celsius) 
print('T.celsius1:', T.celsius1.value) 

What is the purpose of self?

2

Vom documentation:

Die folgenden Verfahren sind nur anwendbar, wenn eine Instanz der Klasse das Verfahren enthält (eine sogenannte Deskriptor Klasse) erscheint in einem Inhaber Klasse (der Deskriptor muss entweder in der Klasse des Besitzers oder im Klassenwörterbuch für einen seiner Eltern sein).

So Deskriptoren (dh. Objekte, die __get__, __set__ oder __delete__ implementieren) müssen Mitglieder einer Klasse sein, keine Instanz.

Mit den followinging Änderungen:

Temperature.celsius2 = Celsius() 

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

Die Ausgabe lautet:

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

Weitere Links:

8

Die Unterschiede liegen in der Tatsache, das das erste Attribut ist ein Attribut Klasse während die zweite eine Instanz Attribut.

Wie pro the documentation Wenn ein Objekt, das die erste der Descriptor Verfahren mindestens implementiert (__get__, __set__ und __delete__) in einer Klasse gehalten wird Attribut, dessen __get__ Methode wird, wenn zugegriffen genannt. Dies ist bei einer Instanz Attribut nicht der Fall. Sie können mehr erfahren from the howto.

__call__ Die Methode eines Objekts kommt nur ins Spiel, wenn das Objekt, wie eine Funktion aufgerufen wird:

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

Bitte schreiben Sie wann (und warum) Objekt als Funktion aufrufen. – IProblemFactory

+0

@ProblemFactory [Erweiterte Dekorator Verwendung] (http://wiki.python.org/moin/PythonDecorators), zum Beispiel. Übergeben eines Objekts in eine "map", "filter" oder "reduce" -Funktion für erweiterte Funktionen. Intelligenter dynamischer Versand Code-Instrumentierung Affen flicken. Die Liste geht weiter und weiter und weiter ... – brice

2

T.celcius als Attribut der Klasse Temperature so dass es das Ergebnis der Methode von T. __get__ kehrt celcius wie erwartet.

T.celsius1 ist ein Attribut der Instanz T, daher gibt es die Variable selbst zurück, da Deskriptoren nur für neue Stilobjekte oder Klassen aufgerufen werden.

Die __call__ Methode würde verwendet werden, wenn Sie T.celsius() tun würden.

0

Wie bereits erwähnt, sollen Deskriptorinstanzen als Klassenattribute verwendet werden.

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

Wenn Sie self.celsius1 wollen individuelle Verhalten haben, außer Kraft setzen __getattr__ Methode:

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