为什么表达式0<

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

查看python 2.6中的queue.py,我发现这个构造有点奇怪:

1
2
3
4
5
6
7
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

如果maxsize为0,则队列永远不会满。

我的问题是它是如何工作的这个案件?如何认为0 < 0 == 0是错误的?

1
2
3
4
5
6
7
8
>>> 0 < 0 == 0
False
>>> (0) < (0 == 0)
True
>>> (0 < 0) == 0
True
>>> 0 < (0 == 0)
True


我相信python对关系运算符序列有特殊的案例处理,以便于表达范围比较。能说0 < x <= 5比说(0 < x) and (x <= 5)好得多。

这些被称为连锁比较。这是他们文档的链接。

对于您所讨论的其他情况,括号强制一个关系运算符先于另一个应用,因此它们不再是链接比较。由于TrueFalse的值是整数,所以可以从带圆括号的版本中得到答案。


因为

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

False。您可以将比较运算符链接在一起,它们将自动展开为成对比较。

edit——在python中澄清对错

在python中,TrueFalse只是bool的实例,它是int的一个子类。换句话说,True实际上只是1。

关键是,您可以像使用整数一样使用布尔比较的结果。这会导致混淆

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

但是,只有在将比较括起来以便首先对它们进行评估时,才会发生这种情况。否则,python将展开比较运算符。


你所经历的奇怪行为来自于Python的连锁能力。因为它发现0不小于0,所以它决定整个表达式的计算结果为false。一旦你把它分解成不同的条件,你就改变了它的功能。最初基本上是在测试a < b && b == c是否符合您最初的a < b == c声明。

另一个例子:

1
2
3
4
5
>>> 1 < 5 < 3
False

>>> (1 < 5) < 3
True


1
2
>>> 0 < 0 == 0
False

这是一个连锁比较。如果每个成对比较依次为真,则返回真。相当于(0 < 0) and (0 == 0)

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

这相当于0 < True,其计算结果为真。

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

这相当于False == 0,其计算结果为真。

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

相当于0 < True,如上所述,其评估结果为真。


从反汇编(字节代码)来看,很明显为什么0 < 0 == 0False

下面是对该表达式的分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
>>>import dis

>>>def f():
...    0 < 0 == 0

>>>dis.dis(f)
  2      0 LOAD_CONST               1 (0)
         3 LOAD_CONST               1 (0)
         6 DUP_TOP
         7 ROT_THREE
         8 COMPARE_OP               0 (<)
        11 JUMP_IF_FALSE_OR_POP    23
        14 LOAD_CONST               1 (0)
        17 COMPARE_OP               2 (==)
        20 JUMP_FORWARD             2 (to 25)
   >>   23 ROT_TWO
        24 POP_TOP
   >>   25 POP_TOP
        26 LOAD_CONST               0 (None)
        29 RETURN_VALUE

注意第0-8行:这些行检查0 < 0是否明显返回False到python堆栈。

现通知11行:JUMP_IF_FALSE_OR_POP 23。这意味着如果0 < 0返回False执行跳到第23行。

现在,0 < 0False,所以进行跳转,将堆栈留给一个False,它是整个表达式0 < 0 == 0的返回值,尽管== 0部分甚至没有检查。

所以,最后,答案就像这个问题的其他答案中所说的那样。0 < 0 == 0有特殊含义。编译器将其评估为两个术语:0 < 00 == 0。就像在它们之间使用and的任何复杂布尔表达式一样,如果第一个表达式失败,那么第二个表达式甚至不会被检查。

希望这能让事情有所启发,我真的希望我用来分析这种意想不到的行为的方法能鼓励其他人在将来尝试同样的方法。


正如其他人提到的,x comparison_operator y comparison_operator z(x comparison_operator y) and (y comparison_operator z)的合成糖,加上y只被评估一次。

所以你的表达式0 < 0 == 0实际上是(0 < 0) and (0 == 0),它的计算结果是False and True,它只是False


也许从这些文件中摘录可以帮助:

These are the so-called"rich
comparison" methods, and are called
for comparison operators in preference
to __cmp__() below. The correspondence
between operator symbols and method
names is as follows: x calls
x.__lt__(y), x<=y calls x.__le__(y),
x==y calls x.__eq__(y), x!=y and x<>y
call x.__ne__(y), x>y calls
x.__gt__(y), and x>=y calls
x.__ge__(y).

A rich comparison method may return
the singleton NotImplemented if it
does not implement the operation for a
given pair of arguments. By
convention, False and True are
returned for a successful comparison.
However, these methods can return any
value, so if the comparison operator
is used in a Boolean context (e.g., in
the condition of an if statement),
Python will call bool() on the value
to determine if the result is true or
false.

There are no implied relationships
among the comparison operators. The
truth of x==y does not imply that x!=y
is false. Accordingly, when defining
__eq__(), one should also define __ne__() so that the operators will behave as expected. See the paragraph
on __hash__() for some important notes
on creating hashable objects which
support custom comparison operations
and are usable as dictionary keys.

There are no swapped-argument versions
of these methods (to be used when the
left argument does not support the
operation but the right argument
does); rather, __lt__() and __gt__()
are each other’s reflection, __le__()
and __ge__() are each other’s
reflection, and __eq__() and __ne__()
are their own reflection.

Arguments to rich comparison methods
are never coerced.

这些都是比较,但是由于您正在链接比较,您应该知道:

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

Formally, if a, b, c, ..., y, z are
expressions and op1, op2, ..., opN are
comparison operators, then a op1 b op2
c ... y opN z is equivalent to a op1 b
and b op2 c and ... y opN z, except
that each expression is evaluated at
most once.


就在这里,在它所有的荣耀中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
>>> class showme(object):
...   def __init__(self, name, value):
...     self.name, self.value = name, value
...   def __repr__(self):
...     return"<showme %s:%s>" % (self.name, self.value)
...   def __cmp__(self, other):
...     print"cmp(%r, %r)" % (self, other)
...     if type(other) == showme:
...       return cmp(self.value, other.value)
...     else:
...       return cmp(self.value, other)
...
>>> showme(1,0) < showme(2,0) == showme(3,0)
cmp(<showme 1:0>, <showme 2:0>)
False
>>> (showme(1,0) < showme(2,0)) == showme(3,0)
cmp(<showme 1:0>, <showme 2:0>)
cmp(<showme 3:0>, False)
True
>>> showme(1,0) < (showme(2,0) == showme(3,0))
cmp(<showme 2:0>, <showme 3:0>)
cmp(<showme 1:0>, True)
True
>>>

我认为Python在魔法之间做的很奇怪。与1 < 2 < 3相同,意味着2在1和3之间。

在这种情况下,我认为它所做的是[中间0]大于[左0]等于[右0]。中间0不大于左0,因此计算结果为false。