2009-11-02 15 views
19

Przyjaciel zwrócił mi na to uwagę, a gdy zwróciłem uwagę na dziwność, obaj jesteśmy zdezorientowani.Co oznacza "ocenione tylko raz" dla przykutych porównań w Pythonie?

Dokumenty Pythona, powiedzmy, i że co najmniej od 2.5.1 (nie zaznaczone na dalsze:

Porównania mogą być dowolnie łączone, na przykład, x < < Y = Z jest równa X < y i y < = z tym wyjątkiem, że y jest oceniany tylko raz (ale w obu przypadkach z nie jest analizowany w ogóle, gdy x < y okaże się fałszywe).

Nasz zamieszanie leży w rozumieniu "y jest oceniane tylko raz".

Biorąc pod uwagę prostą ale contrived Klasa:

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 

Możemy produkować ten wynik:

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

To z pewnością wygląda jak 'y' jest w tradycyjnym sensie "oceniany" dwa razy - - raz, gdy wywoływana jest x.__lt__(y) i przeprowadza porównanie na nim, i raz, gdy wywoływana jest y.__le__(z).

Co należy rozumieć przez to, co dokładnie oznaczają dokumenty Pythona, gdy mówią "y jest oceniane tylko raz"?

Odpowiedz

44

"Wyrażenie" y jest obliczane jednorazowo. Oznacza to, że w następującym wyrażeniu funkcja jest wykonywana tylko jeden raz.

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

W przeciwieństwie do:

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

W kontekście y oceniany y rozumie się jako dowolne ekspresji, które mogą mieć skutki uboczne. Na przykład:

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