Why does the expression 0 < 0 == 0 return False in Python?

Looking into Queue.py in Python 2.6, I found this construct that I found a bit strange:

def full(self):
    """Return True if the queue is full, False otherwise
    (not reliable!)."""
    self.mutex.acquire()
    n = 0 < self.maxsize == self._qsize()
    self.mutex.release()
    return n

If maxsize is 0 the queue is never full.

My question is how does it work for this case? How 0 < 0 == 0 is considered False?

>>> 0 < 0 == 0
False
>>> (0) < (0 == 0)
True
>>> (0 < 0) == 0
True
>>> 0 < (0 == 0)
True

Solution 1:

I believe Python has special case handling for sequences of relational operators to make range comparisons easy to express. It's much nicer to be able to say 0 < x <= 5 than to say (0 < x) and (x <= 5).

These are called chained comparisons. And that's a link to the documentation for them.

With the other cases you talk about, the parenthesis force one relational operator to be applied before the other, and so they are no longer chained comparisons. And since True and False have values as integers you get the answers you do out of the parenthesized versions.

Solution 2:

Because

(0 < 0) and (0 == 0)

is False. You can chain together comparison operators and they are automatically expanded out into the pairwise comparisons.


EDIT -- clarification about True and False in Python

In Python True and False are just instances of bool, which is a subclass of int. In other words, True really is just 1.

The point of this is that you can use the result of a boolean comparison exactly like an integer. This leads to confusing things like

>>> (1==1)+(1==1)
2
>>> (2<1)<1
True

But these will only happen if you parenthesise the comparisons so that they are evaluated first. Otherwise Python will expand out the comparison operators.

Solution 3:

The strange behavior your experiencing comes from pythons ability to chain conditions. Since it finds 0 is not less than 0, it decides the entire expression evaluates to false. As soon as you break this apart into seperate conditions, you're changing the functionality. It initially is essentially testing that a < b && b == c for your original statement of a < b == c.

Another example:

>>> 1 < 5 < 3
False

>>> (1 < 5) < 3
True

Solution 4:

>>> 0 < 0 == 0
False

This is a chained comparison. It returns true if each pairwise comparison in turn is true. It is the equivalent to (0 < 0) and (0 == 0)

>>> (0) < (0 == 0)
True

This is equivalent to 0 < True which evaluates to True.

>>> (0 < 0) == 0
True

This is equivalent to False == 0 which evaluates to True.

>>> 0 < (0 == 0)
True

Equivalent to 0 < True which, as above, evaluates to True.