0

这是关于python语言的。我观察到'apple'中的'a'返回True。但是为什么'apple'中的assert True =='a'会引发断言错误。

4

4 回答 4

6

来自Python 文档

比较可以任意链接,例如,x < y <= z 等价于 x < y 和 y <= z [...]

in==都是比较器,所以你写的代码做了两个比较:True == 'a''a' in 'apple'. 其中第一个是不正确的。

于 2015-01-21T15:39:21.990 回答
2

注意:这个答案让我在 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_POPand

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。

于 2015-01-21T15:34:58.650 回答
1

运算符优先级是原因。尝试这个:

# Python 2.7
assert True==('a' in 'apple')

这不应该引发断言错误。您试图做的是断言True单例等于字符串a,其计算结果为False

话虽如此,如果您实际使用此断言进行测试,则无需将结果与True. 简单地:

assert 'a' in 'apple'
于 2015-01-21T15:34:35.517 回答
0

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哪个是错误的。

于 2015-01-21T15:34:45.420 回答