A friend brought this to my attention, and after I pointed out an oddity, we're both confused.
Python's docs, say, and have said since at least 2.5.1 (haven't checked further back:
Comparisons can be chained arbitrarily, e.g., x < y <= z is equivalent to x < y and y <= z, except that y is evaluated only once (but in both cases z is not evaluated at all when x < y is found to be false).
Our confusion lies in the meaning of "y is evaluated only once".
Given a simple but contrived class:
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
We can produce this result:
>>> 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.
>>>
This certainly looks like 'y' is, in a traditional sense "evaluated" twice -- once when x.__lt__(y)
is called and performs a comparison on it, and once when y.__le__(z)
is called.
So with this in mind, what exactly do the Python docs mean when they say "y is evaluated only once"?
Chaining with the comparison operator == returns True only if all values are equal. If there is even one different value, False is returned. Be careful when using the comparison operator != that returns True when the values are not equivalent.
Python supports chaining of comparison operators, which means if we wanted to find out if b lies between a and c we can do a < b < c , making code super-intuitive. Python evaluates such expressions like how we do in mathematics.
No, there aren't any side-effects.
The 'expression' y
is evaluated once. I.e., in the following expression, the function is executed only one time.
>>> def five():
... print 'returning 5'
... return 5
...
>>> 1 < five() <= 5
returning 5
True
As opposed to:
>>> 1 < five() and five() <= 5
returning 5
returning 5
True
In the context of y being evaluated, y is meant as an arbitrary expression that could have side-effects. For instance:
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With