按位运算符 & 在 Python

Bitwise operator & in Python

最近,我正在解决 Hackerrank 的一个非常基本的条件问题。

问题是:

If n is odd, print Weird
If n is even and in the inclusive range of 2 to 5 print Not Weird
If n is even and in the inclusive range of 6 to 20 print Weird
If n is even and greater than 20, print Not Weird

所以我写了下面的代码

if(n % 2 == 0 & n >=6 & n <= 20 ):
    print("Weird")
elif(n % 2 == 0 & n <= 2 & n >= 5): 
     print("Not Weird")
elif( n % 2 != 0):
     print("Weird")
else:
     print("Not Weird")

一开始没用。我发现我必须使用 "and" 而不是“&”。
但是,我不相信为什么会这样..
对于 n = 12,第一个 if 条件的所有 3 个条件都为真。所以 true & true & true 应该也是真的吧?

this photo shows the code snippet with some print messages

我很想知道这种行为背后的原因,请指导! (可能是非常小的事情,但请不要介意:))

for n = 12, all 3 conditions of the first if condition are true.

>>> n = 12
>>> n % 2 == 0 & n >= 6 & n <= 20
False

第二个条件为假(条件不是你想的那样,因为按位运算符与其他数字运算符相似[+-% , 等],因为它们比比较运算符具有更高的优先级)。

让我们稍微分解一下:

>>> n % 2
0
>>> 0 & n
0
>>> 6 & n
4
>>> 0 == 0 >= 4 <= 20
False

由于 0 & n >= 6 & n 条件,表达式为假。 0 不大于 4!

grammar 中可能不是很明显,但是二进制算术运算比二进制按位运算绑定得更紧密。所以你的比较和

一样
>>> if(n % 2 == (0 & n) >= (6 & n) <= 20 ):
...     print("Weird")
... 
>>>

你的"true & true &true"案例是

>>> if((n % 2 == 0) & (n >=6) & (n <= 20) ):
...     print('Weird')
... 
Weird

但这只有效,因为 python TrueFalse 评估为 10。由于这种底层表示,按位运算才有效。

每次有这样的疑惑,可以使用dis模块,在你的情况下我做了一个lambda函数来评估过程:

func = lambda x: x % 2 == 0 & x >= 6 & x <= 20

这为我们提供了以下反汇编代码:

              0 LOAD_FAST                0 (x)
              2 LOAD_CONST               1 (2)
              4 BINARY_MODULO
              6 LOAD_CONST               2 (0)
              8 LOAD_FAST                0 (x)
             10 BINARY_AND
             12 DUP_TOP
             14 ROT_THREE
             16 COMPARE_OP               2 (==)
             18 JUMP_IF_FALSE_OR_POP    40
             20 LOAD_CONST               3 (6)
             22 LOAD_FAST                0 (x)
             24 BINARY_AND
             26 DUP_TOP
             28 ROT_THREE
             30 COMPARE_OP               5 (>=)
             32 JUMP_IF_FALSE_OR_POP    40
             34 LOAD_CONST               4 (20)
             36 COMPARE_OP               1 (<=)
             38 RETURN_VALUE
        >>   40 ROT_TWO
             42 POP_TOP
             44 RETURN_VALUE

解释只是:

  • 获取变量x
  • 获取常量2
  • 得到modulo比较2x,当你使用12作为参数值时,答案是0
  • 获取常量0
  • 获取变量x
  • 得到0x之间的二进制and比较,自然地,任何值(例如:b'1100')和b'0000'之间的位操作将return 0.
  • 接下来,将值存储在 TOP 中并与 == 运算符进行比较。这意味着此时其他答案说 (x % 2) == (0 & x)x12,翻译后的操作是 0 == 0(真)。
  • 使用 == 结果检查是否为 False(在这种情况下,跳转到字节码的第 40 行)否则,从 TOS(堆栈顶部)中删除结果 (POP)
  • 获取常量6
  • 获取变量x
  • 另一个and比较,这次是6b'0110')和12x=b'1100')之间的结果4 (b'0100')
  • 最后的结果存储在 TOP 中,并与 >= 运算符与最后存储的值进行比较(0 >= 4 如其他答案所述)。
  • 来自 0 >= 4 的响应然后在下一条指令中进行评估,False 导致代码直接跳到指令 40 (Return False),甚至没有测试最后一个案例。

当然,这是 python 如何工作的字节码解释,简短的回答是 0 & n >= 6 & n 的事情,考虑到这一点,我们可以假设每个不同于 0 的数字,将return 此评估为假,因为 b'0110' 之间的按位运算将始终大于 0。

TL;DR

按位运算符在布尔运算符之前计算。

参考资料

https://docs.python.org/3.8/library/dis.html Dis 模块引用。