开发者

What does "evaluated only once" mean for chained comparisons in Python?

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 arbi开发者_JAVA技巧trarily, 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"?


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
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜