5

我今天在代码库中遇到了|=运算符,但没有认出它,并且在谷歌搜索python "|="返回对我没有任何用处。

测试以下操作的结果会产生以下结果:

>>> from itertools import combinations
>>> def wtf_is_it(left, right):
>>>     orig_left = left
>>>     left |= right
>>>     return '%s |= %s == %s ' % (orig_left, right, left)
>>> results = [wtf_is_it(l, r) for l, r in combinations(xrange(5), 2)]
>>> print '\n'.join(results)
0 |= 1 == 1 
0 |= 2 == 2 
0 |= 3 == 3 
0 |= 4 == 4 
1 |= 0 == 1 
1 |= 2 == 3 
1 |= 3 == 3 
1 |= 4 == 5 
2 |= 0 == 2 
2 |= 1 == 3 
2 |= 3 == 3 
2 |= 4 == 6 
3 |= 0 == 3 
3 |= 1 == 3 
3 |= 2 == 3 
3 |= 4 == 7 
4 |= 0 == 4 
4 |= 1 == 5 
4 |= 2 == 6 
4 |= 3 == 7 

看起来它做了一个or,如果右侧大于左侧,那么它将两者相加,否则它保持原始值?

这个运算符的名称是什么,它的值是什么?

4

4 回答 4

13

对于整数,它是按位或复合赋值运算符。像所有复合赋值运算符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_ERRORE_PARSEE_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_PARSEE_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每次之后都将保持设置状态,不会无意中影响其他标志。

(做这一切背后的数学 - 这会很有启发性)

于 2013-09-18T23:38:24.940 回答
2

一个用例是当您需要对多个值进行OR累加时,例如,确定其中一个或多个是True,还是全部为False

这里有几个(人为的)例子。

假设您有一个通过或不及格某个科目的学生列表。如果您需要计算是否学生通过了该科目,您可以编写如下内容:

passed = False

for student in students:
    passed |= student.passed

if passed:
    print("someone passed")
else:
    print("no one passed")

同样,如果您在网页上有一个复选框列表,并且如果选中其中任何一个,您需要拒绝服务,您可以编写如下内容:

deny = False

for box in boxes:
    deny |= box.checked
于 2013-09-18T23:51:29.390 回答
2

@Jestin 在评论中指出“一个好的用例是累积位掩码”,但由于您要求一个用例,我决定给出一个明确的例子,因为我在 PyQt 中使用了这种结构。PyQt4 中的模型必须有一个flags()返回位掩码的方法。我已使用如下代码有条件地构建适当的位掩码:

def flags(self, index):
    if not index.isValid()
        return None

    f = QtCore.ItemIsEnabled
    if index.column() > some_value:
        f |= QtCore.Qt.ItemIsSelectable
    if some_other_condition:
        f |= QtCore.Qt.ItemIsUserCheckable

    return f
于 2013-09-18T23:52:32.880 回答
0

Jedwards 已经回答了这个问题,但我也会添加一个更通用的答案。

在 Python 中,如果您看到以下形式的运算符:

x ¤= y

任何标准二元运算符在哪里¤- 二进制在某种意义上它需要 2 个运算符,就像+ - * / & |它是以下的简写:

x = x ¤ y

Sox |= y等价于x = x | y, where\是“按位或”运算符,就像x += y, x = x + ywhere+是加法一样。

于 2013-09-18T23:47:37.363 回答