2009-11-02 4 views
19

Un amico ha portato questo alla mia attenzione, e dopo aver sottolineato una stranezza, siamo entrambi confusi.Cosa significa "valutato solo una volta" per i confronti concatenati in Python?

documenti di Python, per esempio, e hanno detto almeno dal 2.5.1 (non sono controllati più indietro:

confronti possono essere concatenati arbitrariamente, ad esempio, x < y = z < è equivalente a x < yey < = z, eccetto che y viene valutato solo una volta (ma in entrambi i casi z non viene valutata se x è trovato < y falsa).

nostra confusione sta nel significato di "y viene valutato solo una volta".

dato un semplice ma artificiosa classe:

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 

Possiamo produrre questo risultato:

>>> 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. 
>>> 

Questo certamente sembra come 'y' è, in un certo senso tradizionale "valutato" due volte - - una volta quando viene chiamato x.__lt__(y) e viene eseguito un confronto su di esso e una volta viene chiamato y.__le__(z).

Quindi, con questo in mente, cosa significano esattamente i documenti Python quando dicono "y viene valutato solo una volta"?

risposta

44

L'espressione ' viene valutata una volta. Ad esempio, nell'espressione seguente, la funzione viene eseguita una sola volta.

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

Al contrario di:

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

Nel contesto y corso di valutazione, y è inteso come un'espressione arbitraria che potrebbe avere effetti collaterali. Ad esempio:

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