在python中使用| =的用例

时间:2013-09-18 23:35:31

标签: python operators

我今天在代码库中遇到了|=运营商并且无法识别它,并且搜索谷歌搜索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 个答案:

答案 0 :(得分:12)

对于整数,它是按位OR复合赋值运算符。与所有复合赋值运算符一样,a |= ba = a | b相同。管道是按位OR的符号。

正如user2357112指出的那样,更丰富的数据类型可以overload this,因为他们认为合适。


可能的用例

假设您有一些想要通过慢速网络传输的二进制值。假设您有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存储它的方式error level constants

如果查看链接,第一列中的值全部(除最后一个条目外)值为2 ^ n ,n = 0,1,2,3 .. 0.14。

实际上(2 ^ n )对位域和按位运算具有特殊意义。

查看n的前几个值的值:

 n | 2^n | 2^n (Binary Representation)
---|-----|----
 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 )值分配了不同的错误级别,我们能够通过单个值表示这些标志的任意组合而不会丢失信息。

我们可以通过采用值的按位OR来计算此组合的值,方法与之前相同。在我们的案例中:

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

在python中:

print(1|4|16)
# 21

这种按位OR运算的结果通常称为bitmask

给定一个位掩码和抛出错误的错误值,您可以快速检查它是否是您感兴趣的东西 - 也就是说,它是E_ERRORE_PARSE类型还是E_CORE_ERROR - 您可以快速计算位掩码的按位AND和错误值。

此AND运算的结果将为0(在这种情况下,您对错误不感兴趣/它不是您感兴趣的级别之一)或非零(在这种情况下)它是)。验证这个按位AND操作是否按照我的说法进行操作留给你。

最后,您可能已经注意到,只需添加错误值会产生与您从OR-ing获得的值相同的值。在这种情况下,这是真的。那么为什么使用bitwise-OR而不是简单的加法?

答案:因为按位或运算可以多次包含一个值。 这很难解释,所以请考虑最后一个例子:

您希望编写能够确保“监控”给定错误级别的代码。您可以访问将获取和设置位掩码的函数。

使用添加,您可以编写如下内容:

set_mask(get_mask() + E_PARSE)

但如果E_PARSE的位已经设置,那么实际上取消设置 E_PARSE ,可能更重要的是 >使至少一个附加标志无效 - 可能使位域中的每个标志无效。

显然,这不是你想要的。

相反,如果您使用按位OR运算:

set_mask(get_mask() | E_PARSE)

无论先前是否设置了E_PARSE,无论您执行此操作多少次,E_PARSE都会在每次操作后保持设置,并且不会有其他标记无意中受到影响。

(做这一切背后的数学 - 它非常有启发性)

答案 1 :(得分:2)

一个用例是当你需要累积 OR 而不是多个值时,比方说,决定其中一个或多个是 True ,或者如果所有这些都错误

以下是一些(人为的)例子。

假设您有一份已通过或未通过某个科目的学生名单。如果您需要计算任何学生是否通过了该主题,您可以编写如下内容:

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

答案 2 :(得分: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

答案 3 :(得分:0)

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

在Python中,如果您看到表单的运算符:

x ¤= y

其中¤是任何标准二元运算符 - 二元运算符,它需要2个运算符,如+ - * / & |,它是简写:

x = x ¤ y

因此x |= yx = x | y等效,其中\是“按位或”运算符,正如x += yx = x + y相同,其中+是补充。