为什么断言是真的==' a'在' apple'是python中的断言错误

时间:2015-01-21 15:29:39

标签: python in-operator

这是关于python语言的。我观察到'apple'中的'a'返回True。但是为什么在'apple'中断言True =='a'提出了断言错误。

4 个答案:

答案 0 :(得分:5)

来自Python Docs

  

比较可以任意链接,例如,x <1。 y&lt; = z是等价的   到x&lt; y和y&lt; = z [...]

in==都是比较器,因此您编写的代码会进行两次比较:True == 'a''a' in 'apple'。第一个是不正确的。

答案 1 :(得分:2)

注意:这个答案让我在Python链式条件语的语义上有了一个有趣的旅程。我没有删除我最初的错误答案,而是将我的研究链保留为其下面的一系列EDIT语句。对于真正的答案,您可以跳到底部,阅读评论,或查看这个问题的其他几个有价值的答案。


由于operator precedence的规则。在Python中,==运算符和in运算符具有相同的优先级,因此它们按从左到右的顺序进行计算。换句话说,您已经写了相当于assert( (True == 'a') in 'apple')的内容。从True != 'a'开始,您声明False in 'apple',这也是错误的,并且断言失败。

TrueFalse的布尔比较是多余的,可以消除。更简洁的说法完全相同的是:

assert 'a' in 'apple'

编辑:下面已经指出assert( (True == 'a') in 'apple' )实际上引发了一个不同的异常(TypeError)。我尝试通过以下反汇编来理解这一点:

>>> def orig():
...   assert True == 'a' in 'apple'
... 
>>> def paren():
...   assert( (True == 'a') in 'apple')
... 
>>> import dis
>>> dis.dis(orig)
  2           0 LOAD_GLOBAL              0 (True)
              3 LOAD_CONST               1 ('a')
              6 DUP_TOP             
              7 ROT_THREE           
              8 COMPARE_OP               2 (==)
             11 JUMP_IF_FALSE_OR_POP    23
             14 LOAD_CONST               2 ('apple')
             17 COMPARE_OP               6 (in)
             20 JUMP_FORWARD             2 (to 25)
        >>   23 ROT_TWO             
             24 POP_TOP             
        >>   25 POP_JUMP_IF_TRUE        34
             28 LOAD_GLOBAL              1 (AssertionError)
             31 RAISE_VARARGS            1
        >>   34 LOAD_CONST               0 (None)
             37 RETURN_VALUE        
>>> dis.dis(paren)
  2           0 LOAD_GLOBAL              0 (True)
              3 LOAD_CONST               1 ('a')
              6 COMPARE_OP               2 (==)
              9 LOAD_CONST               2 ('apple')
             12 COMPARE_OP               6 (in)
             15 POP_JUMP_IF_TRUE        24
             18 LOAD_GLOBAL              1 (AssertionError)
             21 RAISE_VARARGS            1
        >>   24 LOAD_CONST               0 (None)
             27 RETURN_VALUE        
>>> 

这实际上表明Python已将比较运算符(==in)解释为可快速失败为False的链,从而导致AssertionError。一旦True == 'a'被评估为False,Python就会推断整个语句必须为false,并在不继续尝试评估False in 'apple'的情况下触发断言失败。当我之前通过添加括号解释时,我强制解释器按顺序评估每个嵌套的括号,因此它不能使用short-circuit evaluation并避免后来的异常。


编辑2:我的短路评估假设似乎也不完整。真正的答案在于Python处理链式条件的方式:作为一系列由布尔and连接的单独布尔比较。这是我上面反汇编的字节码的演练,使用Python提示将解释器的堆栈表示为列表:

>>> stack = [] # The empty stack
>>> stack.append(True) # LOAD_GLOBAL              0 (True)
>>> stack.append('a')  # LOAD_CONST               1 ('a')
>>> stack.append(stack[-1]) # DUP_TOP
>>> stack
[True, 'a', 'a']
>>> stack[-3],stack[-2],stack[-1] = stack[-1], stack[-3], stack[-2] # ROT_THREE
>>> stack
['a', True, 'a']
>>> stack.append(stack[-2] == stack[-1])
>>> stack
['a', True, 'a', False]
>>> # JUMP_IF_FALSE_OR_POP    23

此时,如果第一个条件(True == 'a')为真,我们会将True值从堆栈顶部弹出并继续评估下一个值('a')是in 'apple'(第14和17行)。基于True == 'a' in 'apple'等同于(True == 'a') and ('a' in 'apple')这一事实,我们应该期待这一点。 JUMP_IF_FALSE_OR_POP实现了对布尔and链的短路评估:只要遇到一个错误条件,整个表达式就必须为假。

第20行的JUMP_FORWARD 2 (to 25)表示两个分支汇聚在第25行,这是检查链式条件的最终结果的位置。让我们回顾一下真正的执行链,根据False结果跳转到23:

>>> stack[-2], stack[-1] = stack[-1], stack[-2] # ROT_TWO
>>> stack
['a', True, False, 'a']
>>> stack.pop() # POP_TOP
'a'
>>> stack
['a', True, False]
>>> stack.pop() # POP_JUMP_IF_TRUE  34
False

现在我们达到另一个条件跳转,但我们不接受它,因为结果是False,而不是True。相反,我们继续第28和31行,准备并提升AssertionError

最后一点:作为documentation for the dis library注释,字节码是CPython的实现细节,不保证版本之间的相同。我使用的是Python 2.7.3。

答案 2 :(得分:1)

运营商优先权是原因。试试这个:

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

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

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

assert 'a' in 'apple'

答案 3 :(得分: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,这是错误的。