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步比较a
和b
,然后在第14步比较a
和c
。但为什么它仍然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
。否则评估为 False
。 a==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
被评估为 False
。 1<2
计算为 True
,2<1.5
计算为 False
。 True and
假is 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 14
是 True
那么它将继续执行 8
到 14
。
并且在单个表达式中使用多个布尔运算符是安全的。
从 0
到 8
它比较 a < b
,在 10
检查它是否 False
,如果是则去 18
,旋转堆栈,弹出顶部值,即False
,因为a<b<c
是a<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 无论下一个条件的计算结果是什么。
背景
众所周知,我们最好不要像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步比较a
和b
,然后在第14步比较a
和c
。但为什么它仍然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
。否则评估为 False
。 a==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
被评估为 False
。 1<2
计算为 True
,2<1.5
计算为 False
。 True and
假is 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 14
是 True
那么它将继续执行 8
到 14
。
并且在单个表达式中使用多个布尔运算符是安全的。
从 0
到 8
它比较 a < b
,在 10
检查它是否 False
,如果是则去 18
,旋转堆栈,弹出顶部值,即False
,因为a<b<c
是a<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 无论下一个条件的计算结果是什么。