Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Usecase of |= in python

I came across |= operator today in codebase and did not recognise it, and searching google for python "|=" returns nothing useful for me.

Testing the outcome of operations below yields the following:

>>> 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 

Looks like it does an or and if the right side is greater than the left then it adds the two together, otherwise it maintains the orig value?

What is the name of this operator and what is the value of it?

like image 463
Django Doctor Avatar asked Sep 18 '13 23:09

Django Doctor


People also ask

What does &= in Python mean?

It means bitwise AND operation. Example : x = 5 x &= 3 #which is similar to x = x & 3 print(x)

Why use Bitwise Operators Python?

Python bitwise operators are used to perform bitwise calculations on integers. The integers are converted into binary format and then operations are performed bit by bit, hence the name bitwise operators. Python bitwise operators work on integers only and the final output is returned in the decimal format.

Where Python is used in real life?

One of the prime uses of Python is that it is used by software developers. Python simplifies the software development process for complex apps. It is used for project management, as a support programming language, to build control, and testing.


2 Answers

For integers, it's the bitwise-OR compound assignment operator. Like all compound assignment operators, a |= b is the same thing as a = a | b. And the pipe is the symbol for bitwise OR.

  • Compound Assignment (Augmented Assignment)
  • Bitwise OR

As user2357112 points out, richer datatypes can overload this as they see fit.


Possible use cases

Lets say you had a number of binary values you wanted to transmit over a slow network. Lets say you have the output of 6 switches, each of which can have the values 0 or 1.

For example, you have a list: switches = [0,1,1,0,1,0,0,0]

The naive way to transmit it would be as 6 different integer values. If you used bytes to transmit the values it'd be a total of 6*8 = 48 bits. But you will only derive 6 bits of information on the receiving end -- so the remaining 42 bits are wasted.

Instead, you could use a single byte to store the information of all the switches.

One way to do this would be to iterate over the list, and compute the resulting byte.

For example,

val = 0
for i,s in enumerate(switches):
    val |= (s << i)

print(val)

This snippet prints 22, which if you were to convert to binary you would see it is 10110, or 00010110 -- which is just the values of the list, ordered backwards.

The (s << i) generates an integer which, when expressed in binary, contains a single 1 and the rest 0s. So on each iteration of the loop, the val variable may "accumulate" another bit.


One relatively simple real-life example I can think of off the top of my head is the way PHP stores it's error level constants.

If you look at the link, the values in the first column are all (with the exception of the last entry) values 2^n for n = 0,1,2,3...14.

In fact the (2^n) has special significance for bitfields and bitwise operations.

Looking at the value of this for the first few values of n we get:

 n | 2^n | 2^n (Binary Representation)
---|-----|----
 0 |  1  | 00001
 1 |  2  | 00010
 2 |  4  | 00100
 3 |  8  | 01000
 4 | 16  | 10000

If, in the binary representation, you count from the rightmost position moving left n spots, you'll see that you end at the position of the single 1. This is wholly intentional.

Back to PHP. Lets say you, as a server admin are interested only in a subset of the various error levels. For example, you know that you want to display E_ERROR, E_PARSE, and E_CORE_ERROR, while discarding the rest.

Well, because PHP cleverly assigned the different error levels the (2^n) values we just saw, we're able to express any combination of these flags by a single value without losing information.

And we can compute the value of this combination the same way as before, by taking the bitwise OR of the values. In our case:

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

In python:

print(1|4|16)
# 21

The result of this bitwise OR operation is often called a bitmask.

Given a bitmask and the error value of thrown error, you can quickly check whether it is something you're interested in -- that is, whether it is of type E_ERROR, E_PARSE, or E_CORE_ERROR -- you can quickly compute the bitwise AND of the bitmask and the error value.

The result of this AND operation will either be a 0 (in which case you're not interested in the error / it isn't of one of the levels you're interested in) or non-zero (in which case it is). Verifying that this bitwise AND operation does what I say it does is left to you.

Lastly, you may have noticed that simply adding the error values results in the same value as the one you get from OR-ing. In this case, this is true. So why use bitwise-OR over simple addition?

Answer: because the bitwise-OR operation is resilient to including a value multiple times. This is hard to explain, so consider this final example:

You want to write code that will ensure that a given error level is "monitored". You have access to functions that will get and set the bitmask.

Using addition, you'd write something like:

set_mask(get_mask() + E_PARSE)

But if the bit for E_PARSE were already set, doing this addition would actually unset E_PARSE and possibly more importantly, would invalidate at least one additional flag -- possibly invalidating every flag in your bitfield.

Clearly this isn't what you're looking for.

Instead, if you used the bitwise OR operation:

set_mask(get_mask() | E_PARSE)

regardless of whether E_PARSE were set previously, and no matter how many times you did this operation, E_PARSE would remain set after each, and no other flags would be inadvertently affected.

(Do the math behind all this -- it'd be very instructive)

like image 164
jedwards Avatar answered Sep 23 '22 23:09

jedwards


One use case is when you need to accumulate the OR over a number of values to, let's say, decide if one or more of them is True, or if all of them are False.

Here are a couple of (contrived) examples.

Let's say you have a list of students who have passed or failed a certain subject. If you need to compute whether any student passed that subject, you may write something like:

passed = False

for student in students:
    passed |= student.passed

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

Similarly, if you have a list of checkboxes on a web page, and you need to deny service if any of them is checked, you may write something like:

deny = False

for box in boxes:
    deny |= box.checked
like image 43
Ziffusion Avatar answered Sep 25 '22 23:09

Ziffusion