位运算符
位运算符直接操作整数的二进制表示,在底层数据处理、权限系统、网络协议和性能优化等场景中不可或缺。Python 提供了 &(按位与)、|(按位或)、^(按位异或)、~(按位取反)、<<(左移)、>>(右移)六个位运算符,它们仅适用于整数类型。
二进制基础
理解位运算需要先掌握二进制表示。Python 中可以用 bin() 函数查看整数的二进制形式,前缀 0b 表示二进制字面量。
>>> bin(5)
'0b101'
>>> bin(10)
'0b1010'
>>> 0b1010
10
位运算逐位操作两个整数的二进制位。为了直观展示,以下示例中所有数字都用 4 位二进制表示,实际 Python 整数精度不限。
按位与 &
两个对应位都为 1 时,结果位才为 1;否则为 0。& 常用于提取特定位(掩码操作)或判断权限。
>>> 5 & 3
1
# 二进制视角:
# 5 = 0b0101
# 3 = 0b0011
# & 0b0001 = 1
掩码操作是 & 的典型应用:通过与一个特定位模式的数进行与运算,可以保留感兴趣的位,清除其他位。
>>> flags = 0b1101 # 状态标志
>>> mask = 0b0100 # 只关心第 2 位
>>> flags & mask
4 # 0b0100,第 2 位为 1
>>> bool(flags & mask)
True # 第 2 位被设置
按位或 |
两个对应位只要有一个为 1,结果位就为 1。| 常用于设置特定位或合并权限。
>>> 5 | 3
7
# 二进制视角:
# 5 = 0b0101
# 3 = 0b0011
# | 0b0111 = 7
设置标志位的常见模式:
>>> flags = 0b0000
>>> flags |= 0b0001 # 设置第 0 位
>>> flags |= 0b0100 # 设置第 2 位
>>> bin(flags)
'0b101'
按位异或 ^
两个对应位不同时结果为 1,相同时结果为 0。异或运算有一个重要性质:任何数与自身异或得 0,与 0 异或得自身。
>>> 5 ^ 3
6
# 二进制视角:
# 5 = 0b0101
# 3 = 0b0011
# ^ 0b0110 = 6
异或的经典应用是交换两个数(无需临时变量)和简单加密:
>>> a = 5
>>> b = 3
>>> a = a ^ b # a 现在是 5^3 = 6
>>> b = a ^ b # b 现在是 6^3 = 5
>>> a = a ^ b # a 现在是 6^5 = 3
>>> a, b
(3, 5)
按位取反 ~
~x 将整数的每一位取反,0 变 1,1 变 0。在 Python 中,整数使用补码表示且有无限精度,因此 ~x 等价于 -(x + 1)。
>>> ~5
-6
>>> ~0
-1
>>> ~(-1)
0
# 数学解释:~x == -x - 1
>>> ~100
-101
这个等价关系源于无限精度补码:所有位取反后再加 1 得到相反数,因此取反就是 -(x+1)。
左移 << 与右移 >>
x << n 将 x 的二进制位向左移动 n 位,右侧补 0,等效于 x * 2**n。x >> n 将位向右移动 n 位,左侧补符号位(正数补 0,负数补 1),等效于 x // 2**n。
>>> 5 << 2
20 # 5 * 4 = 20,0b0101 → 0b010100
>>> 20 >> 2
5 # 20 // 4 = 5,0b010100 → 0b0101
>>> 1 << 10
1024 # 2**10,快速计算 2 的幂
右移运算对负数执行算术右移(保留符号位):
>>> -20 >> 2
-5 # -20 // 4 = -5
>>> bin(-20 & 0xFF) # 低 8 位视角
'0b11101100'
权限系统设计
位运算最常见的实际应用是权限系统。Linux 文件权限、数据库权限和 API 权限都广泛使用位掩码。
# 定义权限常量(每个权限占一位)
READ = 1 << 0 # 0b0001 = 1
WRITE = 1 << 1 # 0b0010 = 2
EXECUTE = 1 << 2 # 0b0100 = 4
ADMIN = 1 << 3 # 0b1000 = 8
# 授予权限(按位或)
user_perm = READ | WRITE # 0b0011 = 3
# 检查权限(按位与)
>>> bool(user_perm & READ)
True
>>> bool(user_perm & EXECUTE)
False
# 添加权限
user_perm |= EXECUTE # 现在拥有读、写、执行
# 移除权限(与掩码的反码进行与运算)
user_perm &= ~WRITE # 移除写权限
>>> bool(user_perm & WRITE)
False
# 切换权限(异或)
user_perm ^= ADMIN # 如果没有 ADMIN 则添加,有则移除
这种设计的优势在于:一个整数可以存储任意数量的布尔权限,检查、添加、移除操作都是 O(1) 的位运算。
负数位运算
Python 的整数有无限精度,负数使用补码的无限扩展形式。这导致负数的位运算结果可能与固定宽度语言(如 C 的 32 位 int)不同。
>>> bin(-5)
'-0b101' # Python 的 bin() 显示带符号的简洁形式
# 与掩码配合查看补码表示
>>> bin(-5 & 0xFF)
'0b11111011' # 8 位补码表示
>>> bin(-5 & 0xFFFFFFFF)
'0b11111111111111111111111111111011' # 32 位补码
如果需要模拟固定宽度的位运算(如处理网络协议或硬件寄存器),通常通过与掩码进行与运算来截断:
def uint8(value):
"""将值限制为 8 位无符号整数"""
return value & 0xFF
>>> uint8(-5)
251 # 0b11111011
>>> uint8(300)
44 # 300 & 0xFF = 44
位运算与集合运算的对应
位运算与集合运算存在直接对应关系,这在处理特征向量或标签集合时很有用:
# 集合视角
set_a = {0, 2} # 0b0101 中为 1 的位
set_b = {0, 1} # 0b0011 中为 1 的位
# 交集对应按位与
>>> 5 & 3
1 # 只有位 0 在两个数中都为 1
# 并集对应按位或
>>> 5 | 3
7 # 位 0、1、2 至少在一个数中为 1
# 对称差对应按位异或
>>> 5 ^ 3
6 # 位 1 和 2 只在一个数中为 1
位运算符直接操作数据的底层表示,效率极高。权限系统设计展示了位运算的工程价值,而无限精度整数和补码机制则体现了 Python 在底层灵活性与高层抽象之间的平衡。处理固定宽度协议数据时,记得用掩码截断结果以匹配预期位宽。