在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
答案 0 :(得分:110)
我相信Python对关系运算符的序列进行了特殊的处理,使得范围比较易于表达。能说0 < x <= 5
而不是说(0 < x) and (x <= 5)
要好得多。
这些被称为chained comparisons。这是他们文档的链接。
在你谈到的其他情况下,括号强制一个关系运算符在另一个之前应用,因此它们不再是链式比较。由于True
和False
的值为整数,因此您可以从括号中获得答案。
答案 1 :(得分:40)
由于
(0 < 0) and (0 == 0)
是False
。您可以将比较运算符链接在一起,它们会自动扩展到成对比较中。
编辑 - 澄清Python中的真假
在Python中True
和False
只是bool
的实例,它是int
的子类。换句话说,True
实际上只是1。
这一点是你可以使用与整数完全相同的布尔比较结果。这会导致像
这样令人困惑的事情>>> (1==1)+(1==1)
2
>>> (2<1)<1
True
但是,只有在对比例进行括号处理以便首先进行评估时才会发生这些情况。否则Python将扩展比较运算符。
答案 2 :(得分:18)
你经历的奇怪行为来自蟒蛇连锁条件的能力。由于它发现0不小于0,因此它决定整个表达式的计算结果为false。一旦你将其分解为单独的条件,你就会改变功能。它最初基本上是对a < b && b == c
的原始陈述a < b == c
进行测试。
另一个例子:
>>> 1 < 5 < 3
False
>>> (1 < 5) < 3
True
答案 3 :(得分:8)
>>> 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。
答案 4 :(得分:7)
查看反汇编(字节代码)很明显为什么0 < 0 == 0
是False
。
以下是对此表达式的分析:
>>>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
,因此会跳转,从而为堆栈留下False
,这是整个表达式0 < 0 == 0
的返回值,甚至虽然== 0
部分甚至没有被检查过。
因此,总而言之,答案就像在这个问题的其他答案中所说的那样。
0 < 0 == 0
具有特殊含义。编译器将其评估为两个术语:0 < 0
和0 == 0
。与它们之间有and
的任何复杂布尔表达式一样,如果第一个失败,则第二个也不会被检查。
希望这会对事情有所启发,我真的希望我用来分析这种意外行为的方法将鼓励其他人在将来尝试相同的事情。
答案 5 :(得分:2)
也许docs的摘录可以提供帮助:
这些就是所谓的“富人” 比较“方法,并被称为 用于优先比较运算符 至
__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
如果是的话 没有实现a的操作 给出一对论点。通过 约定,True
和bool()
是 返回进行成功比较。 但是,这些方法可以返回任何方法 值,所以如果是比较运算符 用于布尔上下文(例如,在 if语句的条件), Python会在值上调用x==y
确定结果是否为真或 假的。没有隐含的关系 在比较运算符中。该
x!=y
的真相并不意味着__eq__()
是假的。因此,在定义时__ne__()
,还应定义__hash__()
,以便运算符按预期运行。见段落 在__lt__()
处获取一些重要说明 在创建可用的对象 支持自定义比较操作 并可用作字典键。没有swapped-argument版本 这些方法(将在使用时使用) 左参数不支持 操作但正确的论点 做);相反,
__gt__()
和__le__()
是彼此的反思,__ge__()
和__eq__()
是彼此的 反思,__ne__()
和x < y <= z
是他们自己的反思。丰富比较方法的论据 永远不会被胁迫。
这些是比较,但由于你是chaining comparisons,你应该知道:
比较可以链接 任意地,例如,
x < y and y <= z
是 相当于{{1}},除了 y只评估一次(但在 两个案例z都没有被评估 当x < y被发现是假的。)形式上,如果a,b,c,...,y,z是 表达式和op1,op2,...,opN是 比较运算符,然后是op1 b op2 c ... y opN z相当于op1 b 和b op2 c和... y opN z,除了 每个表达式都在 最多一次。
答案 6 :(得分:2)
正如其他人提到的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
。
答案 7 :(得分:1)
这就是它的荣耀。
>>> 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
>>>
答案 8 :(得分:0)
我认为Python在魔术之间做的很奇怪。与1 < 2 < 3
相同意味着2介于1和3之间。
在这种情况下,我认为它正在[中间0]大于[左0]并且等于[右0]。中间0不大于左0,因此它的计算结果为假。