这是关于python语言的。我观察到'apple'中的'a'返回True。但是为什么'apple'中的assert True =='a'会引发断言错误。
4 回答
来自Python 文档:
比较可以任意链接,例如,x < y <= z 等价于 x < y 和 y <= z [...]
in
和==
都是比较器,所以你写的代码做了两个比较:True == 'a'
和'a' in 'apple'
. 其中第一个是不正确的。
注意:这个答案让我在 Python 链式条件句的语义上玩得很开心。我没有删除我最初的错误答案,而是将我的研究链保留为下面的一系列 EDIT 语句。对于真正的答案,你可以跳到底部,阅读评论,或者查看这个问题的其他几个有价值的答案。
因为运算符优先级的规则。在 Python 中,==
运算符和in
运算符具有相同的优先级,因此它们按从左到右的顺序计算。换句话说,您已经编写了assert( (True == 'a') in 'apple')
. 由于True != 'a'
,您正在断言False in 'apple'
,这也是错误的,并且断言失败。
True
与和的布尔比较False
是多余的,可以消除。表达完全相同的事情的更简洁的方式是:
assert 'a' in 'apple'
编辑:下面已经指出,assert( (True == 'a') in 'apple' )
实际上引发了一个不同的异常(TypeError
)。我试图通过以下反汇编来理解这一点:
>>> 定义原点(): ... 断言 True == 'a' in 'apple' ... >>> 定义括号(): ... assert( (True == 'a') in 'apple') ... >>> 导入磁盘 >>> dis.dis(原版) 2 0 LOAD_GLOBAL 0(真) 3 LOAD_CONST 1 ('a') 6 DUP_TOP 7 ROT_THREE 8 COMPARE_OP 2 (==) 11 跳转_IF_FALSE_OR_POP 23 14 LOAD_CONST 2 ('苹果') 17 COMPARE_OP 6 (in) 20 JUMP_FORWARD 2(至 25) >> 23 ROT_TWO 24 POP_TOP >> 25 POP_JUMP_IF_TRUE 34 28 LOAD_GLOBAL 1(断言错误) 31 RAISE_VARARGS 1 >> 34 LOAD_CONST 0(无) 37 返回值 >>> dis.dis(paren) 2 0 LOAD_GLOBAL 0(真) 3 LOAD_CONST 1 ('a') 6 比较_OP 2 (==) 9 LOAD_CONST 2 ('苹果') 12 COMPARE_OP 6 (in) 15 POP_JUMP_IF_TRUE 24 18 LOAD_GLOBAL 1(断言错误) 21 RAISE_VARARGS 1 >> 24 LOAD_CONST 0(无) 27 返回值 >>>
这实际上表明 Python 已将比较运算符 (==
和in
) 解释为可以快速失败为 False 的链,从而导致AssertionError
. 一旦True == 'a'
评估为 False,Python 就推断整个语句必须为 false,并触发断言失败而不继续尝试评估False in 'apple'
. 前面加括号解释的时候,强制解释器按顺序对每个嵌套的括号求值,所以不能使用短路求值,避免后面的异常。
编辑2:看来我的短路评估假设也不完整。真正的答案在于 Python 处理链式条件的方式:作为一系列由 boolean 连接的单独的布尔比较and
。这是我在上面反汇编的字节码的演练,使用 Python 提示符将解释器的堆栈表示为列表:
>>> stack = [] # 空栈 >>> stack.append(True) # LOAD_GLOBAL 0 (True) >>> stack.append('a') # LOAD_CONST 1 ('a') >>> stack.append(stack[-1]) # DUP_TOP >>> 堆栈 [真,'a','a'] >>> stack[-3],stack[-2],stack[-1] = stack[-1], stack[-3], stack[-2] # ROT_THREE >>> 堆栈 ['a',真,'a'] >>> stack.append(stack[-2] == stack[-1]) >>> 堆栈 ['a',真,'a',假] >>> # JUMP_IF_FALSE_OR_POP 23
此时,如果第True == 'a'
一个条件True
(这是我们应该基于等效于 的事实所期望的。这是实现布尔链的短路评估的方法:一旦遇到一个错误条件,整个表达式就必须为假。'a'
in 'apple'
True == 'a' in 'apple'
(True == 'a') and ('a' in 'apple')
JUMP_IF_FALSE_OR_POP
and
第JUMP_FORWARD 2 (to 25)
20 行表示两个分支会聚在第 25 行,这是检查链式条件的最终结果的地方。让我们回顾一下真正的执行链,根据False
结果跳转到 23:
>>> stack[-2], stack[-1] = stack[-1], stack[-2] # ROT_TWO >>> 堆栈 ['a',真,假,'a'] >>> stack.pop() # POP_TOP '一个' >>> 堆栈 ['a',真,假] >>> stack.pop() # POP_JUMP_IF_TRUE 34 错误的
现在我们到达另一个条件跳转,但我们不接受它,因为结果是False
,不是True
。相反,我们继续第 28 行和第 31 行,它们准备并引发AssertionError
.
最后一点:作为库的文档dis
说明,字节码是CPython的一个实现细节,不保证版本之间是相同的。我正在使用 Python 2.7.3。
运算符优先级是原因。尝试这个:
# Python 2.7
assert True==('a' in 'apple')
这不应该引发断言错误。您试图做的是断言True
单例等于字符串a
,其计算结果为False
。
话虽如此,如果您实际使用此断言进行测试,则无需将结果与True
. 简单地:
assert 'a' in 'apple'
in
和中的比较运算符==
具有相同的优先级(https://docs.python.org/2/reference/expressions.html#operator-precedence),并且语句从左到右评估,单独评估每个二进制链接。等效的表达式是True == 'a' and 'a' in 'apple'
(https://docs.python.org/3.5/reference/expressions.html#comparisons),因此您正在测试False And True
哪个是错误的。