为什么expression式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
被认为是False?
>>> 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)
要好得多。
这些被称为链式比较 。 这是他们的文档链接。
在你谈到的其他案例中,括号强制一个关系运算符先于另一个运用,因此它们不再链接比较。 由于True
和False
具有整数值,所以您可以从括号内find答案。
因为
(0 < 0) and (0 == 0)
是False
。 您可以将比较运算符链接在一起,并自动扩展到成对比较中。
编辑 – 澄清在Python中的真和假
在Python中, True
和False
只是bool
实例,它是int
一个子类。 换句话说,真的只是1。
这一点就是你可以像整数一样使用布尔比较的结果。 这导致混乱的事情
>>> (1==1)+(1==1) 2 >>> (2<1)<1 True
但是这些只有在括号比较的时候才会发生,所以先评估它们。 否则,Python将扩大比较运算符。
你所经历的奇怪的行为来自python能力链条件。 由于它发现0不小于0,它决定整个expression式的计算结果为false。 只要你把这个分解成独立的条件,你正在改变function。 它最初基本上是testing你a < b == c
原始语句a < b && b == c
a < b == c
。
另一个例子:
>>> 1 < 5 < 3 False >>> (1 < 5) < 3 True
>>> 0 < 0 == 0 False
这是一个链接比较。 如果每个两两比较依次为真,则返回true。 它相当于(0 < 0) and (0 == 0)
>>> (0) < (0 == 0) True
这相当于0 < True
,其值为True。
>>> (0 < 0) == 0 True
这相当于False == 0
,其值为True。
>>> 0 < (0 == 0) True
相当于0 < True
,如上所述,计算结果为True。
看反汇编(字节代码)很明显为什么0 < 0 == 0
是False
。
这是对这个expression式的分析:
>>>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 < 0
是False
,所以跳转被取消,即使没有检查到== 0
部分,也会将整个expression式0 < 0 == 0
的返回值False
留给堆栈。
所以,总而言之,答案就好像在这个问题的其他答案中所说的那样。 0 < 0 == 0
具有特殊的含义。 编译器将其评估为两个项: 0 < 0
和0 == 0
。 和其他任何复杂的布尔expression式一样,如果第一个失败,那么第二个就不会被检查。
希望这能使事情发展一点,我真的希望我用来分析这种意外行为的方法会鼓励其他人在未来尝试相同的方法。
正如其他人所提到的那样x comparison_operator y comparison_operator z
是(x comparison_operator y) and (y comparison_operator z)
语法糖,其中y仅被评估一次。
所以你的expression式0 < 0 == 0
实际上是(0 < 0) and (0 == 0)
,它的计算结果是False and True
,这只是False
。
也许这从文档摘录可以帮助:
这些是所谓的“丰富比较”方法,并且比较运算符优先于
__cmp__()
。 运算符与方法名称的对应关系如下:x<y
调用x.__lt__(y)
,x<=y
调用x.__le__(y)
,x==y
调用x.__eq__(y)
,x!=y
和x<>y
调用x.__ne__(y)
,x>y
调用x.__gt__(y)
,x>=y
调用x.__ge__(y)
。一个丰富的比较方法可能会返回单例
NotImplemented
如果它没有实现给定的一对参数的操作。 按照惯例,False
和True
被返回以便成功比较。 但是,这些方法可以返回任何值,所以如果在布尔上下文中使用比较运算符(例如,在if语句的情况下),Python将调用该值的bool()
以确定结果是true还是false 。比较运算符之间没有隐含的关系。
x==y
的真值并不意味着x!=y
是错误的。 因此,在定义__eq__()
,还应该定义__ne__()
以便操作符按预期行事。 有关创build支持自定义比较操作的可__hash__()
对象的一些重要注意事项,请参阅__hash__()
上的段落,并可用作字典键。这些方法没有交换参数的版本(当左边的参数不支持这个操作,但是正确的参数是这样的)。 相反,
__lt__()
和__gt__()
是彼此的reflection,__le__()
和__ge__()
是彼此的reflection,__eq__()
__ne__()
和__ne__()
是他们自己的reflection。对丰富的比较方法的争论从未被强制。
这些是比较,但由于你是链接比较,你应该知道:
比较可以任意链接,例如,
x < y <= z
相当于x < y and y <= z
,只是y只计算一次(但是在两种情况下,当x <y时,根本不计算z是假的)。forms上,如果a,b,c,…,y,z是expression式,op1,op2,…,opN是比较运算符,则op1 b op2 c … y opN z相当于op1 b和b op2 c和… y opN z,除了每个expression式最多被评估一次。
这是它的荣耀。
>>> 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。