为什么表达式0 < 0 = = 0在 Python 中返回 False?

查看 Python 2.6中的 Queue.py,我发现这个结构有点奇怪:

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被认为是假?

>>> 0 < 0 == 0
False
>>> (0) < (0 == 0)
True
>>> (0 < 0) == 0
True
>>> 0 < (0 == 0)
True
9000 次浏览

我相信 Python 对关系运算符序列有特殊的大小写处理,使范围比较易于表达。能够说 0 < x <= 5比说 (0 < x) and (x <= 5)要好得多。

这些被称为 链式比较,这是它们的文档链接。

对于你们谈到的其他情况,括号强迫一个关系运算子在另一个之前应用,因此它们不再是链式比较。因为 TrueFalse的值都是整数,所以你可以从括号中得到答案。

你所经历的奇怪行为来自于蟒蛇的连锁反应能力。因为它发现0不小于0,所以它决定整个表达式的计算结果为 false。一旦你把它分解成不同的条件,你就改变了它的功能。它最初实质上是为 a < b == c的原始语句测试 a < b && b == c

另一个例子:

>>> 1 < 5 < 3
False


>>> (1 < 5) < 3
True

因为

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

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


EDIT —— Python 中真假的澄清

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

这样做的意义在于,您可以像使用整数一样使用布尔比较的结果。这会导致一些令人困惑的事情,比如

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

但是,只有在比较中加上括号,以便首先对它们进行评估时,才会出现这种情况。否则 Python 将扩展比较运算符。

我认为 Python 的做法很奇怪,就像 1 < 2 < 3表示2介于1和3之间。

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

也许下面这段来自 医生的片段能够有所帮助:

这些就是所谓的“有钱人” 方法,并调用 用于优先的比较运算符 到下面的 __cmp__()。信件 在运算符符号和方法之间 名称如下: x<y呼叫 x<=y呼叫 x.__le__(y), x==y调用 x.__eq__(y)x!=yx<>y 呼叫 x.__ne__(y)x>y呼叫 x.__gt__(y)x>=y呼叫 x.__ge__(y).

可能会返回一个丰富的比较方法 单点 NotImplemented,如果它 对象的操作 给定一对论点 惯例,FalseTrue是 回来进行成功的比较。 但是,这些方法可以返回任何 值,因此如果比较运算符 在布尔上下文中使用(例如,在 If 语句的条件) , Python 将对该值调用 bool() 以确定结果是否为真或 假的。

没有隐含的关系 在比较运算符之间 真理的 x==y并不意味着 x!=y 是错误的。因此,当定义 我们还应该定义 __ne__(),这样操作符就会按预期的方式运行 __hash__()上的一些重要音符 创建散列对象 支持自定义比较操作 并可用作字典键。

没有交换参数版本 这些方法(当 左参数不支持 除了正确的参数 而是 __lt__()__gt__() 是彼此的倒影,__le__()__ge__()是对方的 反射,以及 __eq__()__ne__() 是他们自己的倒影。

丰富比较方法的参数 永远不会被强迫。

这些都是比较,但是因为你是 链式比较,你应该知道:

比较可以是链式的 例如,x < y <= z是 等同于 x < y and y <= z,除非 只计算 y 一次(但在 两种情况都没有计算 z 当 x < y 被发现为假时)。

形式上,如果 a,b,c,... ,y,z 是 表达式和 op1,op2,... ,opN 是 比较运算符,然后是 op1 b op2 C... y opN z 等于 a op1 b 还有 b op2c 和... y opNz 除了 每个表达式的计算值是 最多一次。

>>> 0 < 0 == 0
False

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

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

这相当于计算结果为 True 的 0 < True

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

这相当于计算结果为 True 的 False == 0

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

等效于 0 < True,如上所述,计算结果为 True。

正如其他人提到的,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

在这里,它的所有荣耀。

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

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

下面是对这个短语的分析:

>>>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是否显然返回到 python 堆栈中的 False

现在注意第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,如果第一个表达式失败,那么第二个表达式甚至不会被检查。

希望这能给我们一些启发,我真的希望我用来分析这种意想不到的行为的方法能够鼓励其他人在将来也这样做。