【问题标题】:why is assert True=='a' in 'apple' is an assertion error in python为什么'apple'中的assert True = ='a'是python中的断言错误
【发布时间】:2015-01-21 15:29:38
【问题描述】:

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

【问题讨论】:

  • 没有必要写assert True == ('a' in 'apple') assert 'a' in 'apple' 就可以了。
  • @georg,dups 通常是在他们被欺骗的问题之后被问到的。这个问题是因为这个问题而产生的。
  • @PadraicCunningham:我第一次看到那个,这就是原因。

标签: python in-operator


【解决方案1】:

来自Python Docs

比较可以任意链接,例如,x

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

【讨论】:

    【解决方案2】:

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


    因为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)。我试图通过以下反汇编来理解这一点:

    >>> 定义原点(): ... 断言 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'。前面加括号解释的时候,强制解释器按顺序计算每个嵌套的括号,所以不能使用short-circuit evaluation,避免后面的异常。


    编辑 2:我的短路评估假设似乎也不完整。真正的答案在于 Python 处理链式条件的方式:作为由布尔 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'(行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 >>> 堆栈 ['a',真,假,'a'] >>> stack.pop() # POP_TOP '一种' >>> 堆栈 ['a',真,假] >>> stack.pop() # POP_JUMP_IF_TRUE 34 错误的

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

    最后一点:正如documentation for the dis library 所指出的,字节码是CPython 的一个实现细节,不保证在版本之间是相同的。我正在使用 Python 2.7.3。

    【讨论】:

    • 等等,(True == 'a') in 'apple' 不是 False,它会引发异常。
    • 这是不正确的。它们不是从左到右评估的,而是与例如相同的方式。 x < y < z,即为(True == 'a') and ('a' in 'apple') 第一部分为False,所以断言失败。
    • @NPE 感谢您指出这一点。我进一步挖掘并确切地找到了 tobias_k 在他的评论中所说的内容。查看我的编辑。
    • @tobias_k 感谢您指出这一点!我在编辑中解决了这个问题,并在此过程中学到了一些东西。
    • 您的编辑仍然不正确。这里的问题不是短路,而是链式比较被评估为(True == 'a') and ('a' in 'apple')(即使没有短路,它也不会尝试评估False in 'apple')。
    【解决方案3】:

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

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

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

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

    assert 'a' in 'apple'
    

    【讨论】:

      【解决方案4】:

      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,这是错误的。

      【讨论】:

      • 等等,(True == 'a') in 'apple' 不是 False,它会引发异常。
      猜你喜欢
      • 2018-07-20
      • 1970-01-01
      • 1970-01-01
      • 2011-08-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多