对于整数,它是按位或复合赋值运算符。像所有复合赋值运算符a |= b
一样,与a = a | b
. 管道是按位或的符号。
正如user2357112指出的那样,更丰富的数据类型可以在他们认为合适的时候重载它。
可能的用例
假设您有许多想要通过慢速网络传输的二进制值。假设您有 6 个开关的输出,每个开关的值可以是 0 或 1。
例如,您有一个列表:switches = [0,1,1,0,1,0,0,0]
传输它的简单方法是 6 个不同的整数值。如果您使用字节来传输值,则总共是 6*8 = 48 位。但是你只会在接收端获得 6 位信息——所以剩下的 42 位被浪费了。
相反,您可以使用单个字节来存储所有交换机的信息。
一种方法是遍历列表,并计算结果字节。
例如,
val = 0
for i,s in enumerate(switches):
val |= (s << i)
print(val)
此代码段打印22
,如果您将其转换为二进制,您会看到它是 10110 或 00010110 - 这只是列表的值,向后排序。
生成一个整数,当(s << i)
以二进制表示时,它包含一个 1 和其余的 0。因此,在循环的每次迭代中,val
变量可能会“累积”另一位。
我能想到的一个相对简单的现实生活示例是 PHP 存储它的错误级别常量的方式。
如果您查看链接,第一列中的值都是(最后一个条目除外)值 2^ n,n = 0,1,2,3...14。
事实上, (2^ n ) 对于位域和按位运算具有特殊意义。
查看 n 的前几个值的 this 值,我们得到:
n | 2^n | 2^n(二进制表示)
---|-----|----
0 | 1 | 00001
1 | 2 | 00010
2 | 4 | 00100
3 | 8 | 01000
4 | 16 | 10000
如果在二进制表示中,你从最右边的位置开始向左移动n 个点,你会看到你在单个 1 的位置结束。这完全是故意的。
回到 PHP。假设您作为服务器管理员只对各种错误级别的子集感兴趣。例如,您知道要显示E_ERROR
、E_PARSE
和E_CORE_ERROR
,而丢弃其余部分。
好吧,因为 PHP 巧妙地为我们刚刚看到的 (2^ n ) 值分配了不同的错误级别,所以我们能够用单个值表示这些标志的任意组合而不会丢失信息。
我们可以像以前一样计算这个组合的值,方法是对这些值进行按位或。在我们的例子中:
E_ERROR | 1 | 0000 0000 0000 0001
E_PARSE | 4 | 0000 0000 0000 0100
E_CORE_ERROR | 16 | 0000 0000 0001 0000
--------------------------------------
21 0000 0000 0001 0101
在蟒蛇中:
print(1|4|16)
# 21
这种按位或运算的结果通常称为位掩码。
给定一个位掩码和 throwed error 的错误值,您可以快速检查它是否是您感兴趣的东西——也就是说,它是否是 , 或 类型E_ERROR
——E_PARSE
您E_CORE_ERROR
可以快速计算位掩码的按位与和错误值。
此 AND 操作的结果将是 0(在这种情况下您对错误不感兴趣/它不是您感兴趣的级别之一)或非零(在这种情况下是) . 验证这个按位与操作是否按照我所说的那样做是留给你的。
最后,您可能已经注意到,简单地添加错误值会产生与您从 OR-ing 获得的值相同的值。在这种情况下,这是真的。那么为什么要使用按位或而不是简单的加法呢?
回答:因为按位或运算可以灵活地包含一个值多次。这很难解释,所以考虑最后一个例子:
你想编写代码来确保给定的错误级别被“监控”。您可以访问将获取和设置位掩码的函数。
使用加法,您将编写如下内容:
set_mask(get_mask() + E_PARSE)
但是,如果E_PARSE
已经设置了 for 的位,那么执行此添加实际上会取消设置 E_PARSE
,并且可能更重要的是,会使至少一个附加标志无效 - 可能会使您的位域中的每个标志都无效。
显然这不是你要找的。
相反,如果您使用按位或运算:
set_mask(get_mask() | E_PARSE)
无论之前是否E_PARSE
设置过,无论您执行此操作多少次,E_PARSE
每次之后都将保持设置状态,不会无意中影响其他标志。
(做这一切背后的数学 - 这会很有启发性)