print 'a' in 'ab'
打印True
,而
print 'a' in 'ab' == True
打印False
。
有什么猜测?
答案 0 :(得分:5)
>>> 'a' in 'ab' == True
False
>>> ('a' in 'ab') == True
True
让我们来看看实际的第一个变体是什么意思:
>>> import dis
>>> def f():
... 'a' in 'ab' == True
...
>>> dis.dis(f)
2 0 LOAD_CONST 1 ('a')
3 LOAD_CONST 2 ('ab')
6 DUP_TOP
7 ROT_THREE
8 COMPARE_OP 6 (in)
11 JUMP_IF_FALSE_OR_POP 23
14 LOAD_GLOBAL 0 (True)
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
如果您遵循字节码,您将看到这相当于'a' in 'ab' and 'ab' == True
。
LOAD_CONST
之后,堆栈包括:
'ab'
'a'
DUP_TOP
之后,堆栈包括:
'ab'
'ab'
'a'
ROT_THREE
之后,堆栈包括:
'ab'
'a'
'ab'
in
(COMPARE_OP
)对前两个元素进行操作,如'a' in 'ab'
。此结果(True
)存储在堆栈中,同时弹出'ab'
和'a'
。现在堆栈包括:
True
'ab'
JUMP_IF_FALSE_OR_POP
是and
的短路:如果此时堆栈顶部的值为False
,我们知道整个表达式将为False
,所以我们跳过第二次即将到来的比较。但是,在这种情况下,最高值为True
,因此我们弹出此值并继续进行下一次比较。现在堆栈包括:
'ab'
LOAD_GLOBAL
加载True
,COMPARE_OP
(==
)将此与剩余的堆叠项'ab'
进行比较,如'ab' == True
中所示。其结果存储在堆栈中,这是整个语句的结果。(你可以找到完整的字节码指令列表here)
总而言之,我们有'a' in 'ab' and 'ab' == True
。
现在,'ab' == True
为False
:
>>> 'ab' == True
False
表示整个表达式为False
。
答案 1 :(得分:5)
'a' in 'ab' == True
相当于
'a' in 'ab' and 'ab' == True
看看:
>>> 'a' in 'ab' == True
False
>>> ('a' in 'ab') == True
True
>>> 'a' in ('ab' == True)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: argument of type 'bool' is not iterable
>>> 'a' in 'ab' and 'ab' == True
False
从上面链接的文档:
比较可以任意链接,例如,x <1。 y&lt; = z是等价的 到x&lt; y和y&lt; = z,除了y仅被评估一次(但在两者中) 当x
形式上,如果a,b,c,...,y,z是表达式,op1,op2,...,opN 是比较运算符,然后是op1 b op2 c ... y opN z是等价的 到op1 b和b op2 c和... y opN z,除了每个表达式都是 最多评估一次。
操作符链接的真正优点是每个表达式最多只评估一次。因此,使用a < b < c
时,b
仅进行一次评估,然后首先与a
进行比较,然后进行比较(如果必要的话)c
。
作为一个更具体的例子,我们考虑表达式0 < x < 5
。在语义上,我们的意思是说 x处于闭合范围[0,5] 。 Python通过评估逻辑上等效的表达式0 < x and x < 5
来捕获这一点。希望澄清操作员链接的目的。
答案 2 :(得分:2)
原因是Python将a <op1> b <op2> c
视为a <op1> b and b <op2> c
。例如,如果0 < i < n
和 0 < i
,则i < n
为真。因此没有括号的版本会检查'a' in 'ab'
(此部分是否为真)和 'ab' == True
(此部分为false,因此整个表达式为false)。
“简单”修复是一对括号,因为它是上述行为的逃避舱口,但更好的选择是不明确地与布尔值进行比较。