Was bedeutet "Nur einmal ausgewertet" für verkettete Vergleiche in Python?

Ein Freund machte mich darauf aufmerksam, und nachdem ich auf eine Kuriosität hingewiesen hatte, waren wir beide verwirrt.

Pythons Dokumente sagen und haben seit mindestens 2.5.1 gesagt (haben nicht weiter zurück nachgesehen:

Vergleiche können beliebig verkettet werden, z. B. x <y <= z ist äquivalent zu x <y und y <= z, außer dass y nur einmal ausgewertet wird (aber in beiden Fällen wird z überhaupt nicht ausgewertet, wenn x <y gefunden wird falsch sein).

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

Bei einer einfachen, aber erfundenen Klasse:

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 können dieses Ergebnis erzeugen:

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

Das sicherlichsieht aus wie 'y' wird im traditionellen Sinne zweimal "ausgewertet" - einmal wennx.__lt__(y) wird aufgerufen und führt einen Vergleich durch, und zwar einmal wanny.__le__(z) wird genannt.

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

Antworten auf die Frage(2)

Ihre Antwort auf die Frage