Python 在布尔表达式中使用多个运算符

Python use multiple operators in a boolean expression

背景

众所周知,我们最好不要像a = b = [1,2,3]一样使用chain assignment分配多个变量,因为a将是b的浅拷贝。这是不安全的,因为 a 会在我们修改 b 时发生变化。

然而,如果初始化是不可变的,我们可以这样做a = b = 1而且它是安全的。

最近,我发现在控制流的条件表达式中使用多个运算符的奇怪用法,如if 1 < b < 2:while a == b == c == 1:

例如下面的控制流在不同的条件下执行不同的块:

a = 1
b = 1
c = 2

if a == b == c == 1:
    print('All equal!')
else:
    print('At least one variable is not equal to others')

At least one variable is not equal to others


我的问题

控制流中的布尔表达式中,这种多重操作用法安全吗?我知道我们在编写 布尔表达式 时应该检查 operator precedence还有什么需要注意的吗?我试了一段时间,觉得多运算符的用法是安全的。


字节码分析

我键入以下程序的字节码

a = 1;b =2;c =1.5
a<b<c
import dis
dis.dis('a<b<c')
  1           0 LOAD_NAME                0 (a)
              2 LOAD_NAME                1 (b)
              4 DUP_TOP
              6 ROT_THREE
              8 COMPARE_OP               0 (<)
             10 JUMP_IF_FALSE_OR_POP    18
             12 LOAD_NAME                2 (c)
             14 COMPARE_OP               0 (<)
             16 RETURN_VALUE
>   18 ROT_TWO
             20 POP_TOP
             22 RETURN_VALUE```

我只能认识到它在第10步比较ab,然后在第14步比较ac。但为什么它仍然return False。我不熟悉分析字节码。如果有人可以帮助分析它,我将不胜感激!这是Module: dis

的官方指南

这是您提供的代码。

a = 1
b = 1
c = 2

if a == b == c == 1:
    print('All equal!')
else:
    print('At least one variable is not equal to others')

让我们理解它的意思。只有当它们三个都等于1时,a==b==c==1才被评估为True。否则评估为 Falsea==b==c 被评估为 a==b and b==c and c==a.

要得到你想要的,你必须这样做。

if a==b==c==1:
    print('All are equal')
elif (a==b) or (b==c) or (c==a):
   print('At least one variable is not equal to others')
else:
    print('none of them are equal')

现在分析你提供的字节码。

a = 1;b =2;c =1.5
a<b<c

a<b<c 被评估为 a<b and b<c 在你的情况下这是 1<2 and 2<1.5 被评估为 False1<2 计算为 True2<1.5 计算为 FalseTrue andis evaluated to假`。

字节码:

In [2]: a=1;b=2;c=1.5

In [3]: dis.dis('a<b and b<c')
  1           0 LOAD_NAME                0 (a)
              2 LOAD_NAME                1 (b)
              4 COMPARE_OP               0 (<)
              6 JUMP_IF_FALSE_OR_POP    14
              8 LOAD_NAME                1 (b)
             10 LOAD_NAME                2 (c)
             12 COMPARE_OP               0 (<)
        >>   14 RETURN_VALUE

6 JUMP_IF_FALSE_OR_POP 14 这行告诉我们的是,如果为假则跳转到第 14 行。 在 logical and False and any_bool_value 中总是计算为 False.

现在如果 6 JUMP_IF_FALSE_OR_POP 14True 那么它将继续执行 814

并且在单个表达式中使用多个布尔运算符是安全的

08 它比较 a < b,在 10 检查它是否 False,如果是则去 18,旋转堆栈,弹出顶部值,即False,因为a<b<ca<b and b<c,所以如果第一个值是False,不需要检查第二个条件。

但在这种情况下a < b == True,所以它继续。此时它已经通过了第一个检查点(10),它知道第一个条件必须是 True 所以它 returns 无论 b < c 条件的值是什么, 即 False, 所以你得到 False.

相反,如果你检查'a的反汇编

  1           0 LOAD_NAME                0 (a)
              2 LOAD_NAME                1 (b)
              4 COMPARE_OP               0 (<)
              6 JUMP_IF_TRUE_OR_POP     14
              8 LOAD_NAME                1 (b)
             10 LOAD_NAME                2 (c)
             12 COMPARE_OP               0 (<)
        >>   14 RETURN_VALUE

它做相反的事情,检查 (6) 如果第一个条件是 True,如果是,则下一个条件的计算结果和 returns 的值无关紧要, 否则,returns 无论下一个条件的计算结果是什么。