2009-11-02 7 views
19

Ein Freund hat mich darauf aufmerksam gemacht, und nachdem ich auf eine Kuriosität hingewiesen habe, sind wir beide verwirrt.Was bedeutet "nur einmal bewertet" für verkettete Vergleiche in Python?

Python-Dokumente, sagen wir, und die seit mindestens 2.5.1 (nicht weiter nach hinten überprüft:

Vergleiche können beliebig verkettet werden, beispielsweise < x y = z < zu x äquivalent < y und y < = z, mit der Ausnahme, dass y nur einmal ausgewertet wird (aber in beiden Fällen z gar nicht ausgewertet, wenn x < y als falsch gefunden wird).

Unsere Verwirrung liegt in der Bedeutung von "y wird nur einmal ausgewertet."

eine einfache, aber erfundene Klasse Gegeben:

class Magic(object): 
    def __init__(self, name, val): 
     self.name = name 
     self.val = val 
    def __lt__(self, other): 
     print("Magic: Called lt on {0}".format(self.name)) 
     if self.val < other.val: 
      return True 
     else: 
      return False 
    def __le__(self, other): 
     print("Magic: Called le on {0}".format(self.name)) 
     if self.val <= other.val: 
      return True 
     else: 
      return False 

wir dieses Ergebnis produzieren kann:

>>> x = Magic("x", 0) 
>>> y = Magic("y", 5) 
>>> z = Magic("z", 10) 
>>> 
>>> if x < y <= z: 
...  print ("More magic.") 
... 
Magic: Called lt on x 
Magic: Called le on y 
More magic. 
>>> 

Dies ist sicherlich sieht wie 'y' ist, in einem traditionellen Sinn "bewertet" zweimal - - einmal wenn x.__lt__(y) aufgerufen wird und führt einen Vergleich darüber, und einmal wenn y.__le__(z) aufgerufen wird.

Also was genau bedeuten die Python-Dokumente, wenn sie sagen "y wird nur einmal ausgewertet"?

Antwort

44

Der 'Ausdruck' y wird einmal ausgewertet. D. h., In dem folgenden Ausdruck wird die Funktion nur einmal ausgeführt. Als

>>> def five(): 
... print 'returning 5' 
... return 5 
... 
>>> 1 < five() <= 5 
returning 5 
True 

Gegensatz zu:

>>> 1 < five() and five() <= 5 
returning 5 
returning 5 
True 
8

Im Zusammenhang mit y ausgewertet wird, wird als y ein beliebiger Ausdruck gemeint, die Nebenwirkungen haben könnte. Zum Beispiel:

class Foo(object): 
    @property 
    def complain(self): 
     print("Evaluated!") 
     return 2 

f = Foo() 
print(1 < f.complain < 3) # Prints evaluated once 
print(1 < f.complain and f.complain < 3) # Prints evaluated twice