What does |= (ior) do in Python?
In Python, and many other programming languages, |
is the bitwise-OR operation. |=
is to |
as +=
is to +
, i.e. a combination of operation and asignment.
So var |= value
is short for var = var | value
.
A common use case is to merge two sets:
>>> a = {1,2}; a |= {3,4}; print(a)
{1, 2, 3, 4}
|=
performs an in-place+ operation between pairs of objects. In particular, between:
- sets: a union operation
- dicts: an update operation
- counters: a union (of multisets) operation
- numbers: a bitwise OR, binary operation
In most cases, it is related to the |
operator. See examples below.
Sets
For example, the union of two assigned sets s1
and s2
share the following equivalent expressions:
>>> s1 = s1 | s2 # 1
>>> s1 |= s2 # 2
>>> s1.__ior__(s2) # 3
where the final value of s1
is equivalent either by:
- an assigned OR operation
- an in-place OR operation
- an in-place OR operation via special method++
Example
Here we apply OR (|
) and the in-place OR (|=
) to sets:
>>> s1 = {"a", "b", "c"}
>>> s2 = {"d", "e", "f"}
>>> # OR, |
>>> s1 | s2
{'a', 'b', 'c', 'd', 'e', 'f'}
>>> s1 # `s1` is unchanged
{'a', 'b', 'c'}
>>> # In-place OR, |=
>>> s1 |= s2
>>> s1 # `s1` is reassigned
{'a', 'b', 'c', 'd', 'e', 'f'}
Dictionaries
In Python 3.9+, new merge (|
) and update (|=
) operators are proposed between dictionaries. Note: these are not the same as set operators mentioned above.
Given operations between two assigned dicts d1
and d2
:
>>> d1 = d1 | d2 # 1
>>> d1 |= d2 # 2
where d1
is equivalent via:
- an assigned merge-right operation
- an in-place merge-right (update) operation; equivalent to
d1.update(d2)
Example
Here we apply merge (|
) and update (|=
) to dicts:
>>> d1 = {"a": 0, "b": 1, "c": 2}
>>> d2 = {"c": 20, "d": 30}
>>> # Merge, |
>>> d1 | d2
{"a": 0, "b": 1, "c": 20, "d": 30}
>>> d1
{"a": 0, "b": 1, "c": 2}
>>> # Update, |=
>>> d1 |= d2
>>> d1
{"a": 0, "b": 1, "c": 20, "d": 30}
Counters
The collections.Counter
is related to a mathematical datastructure called a multiset (mset). It is basically a dict of (object, multiplicity) key-value pairs.
Given operations between two assigned counters c1
and c2
:
>>> c1 = c1 | c2 # 1
>>> c1 |= c2 # 2
where c1
is equivalent via:
- an assigned union operation
- an in-place union operation
A union of multisets contains the maximum multiplicities per entry. Note, this does not behave the same way as between two sets or between two regular dicts.
Example
Here we apply union (|
) and the in-place union (|=
) to Counters:
import collections as ct
>>> c1 = ct.Counter({2: 2, 3: 3})
>>> c2 = ct.Counter({1: 1, 3: 5})
>>> # Union, |
>>> c1 | c2
Counter({2: 2, 3: 5, 1: 1})
>>> c1
Counter({2: 2, 3: 3})
>>> # In-place Union, |=
>>> c1 |= c2
>>> c1
Counter({2: 2, 3: 5, 1: 1})
Numbers
Lastly, you can do binary math.
Given operations between two assigned numbers n1
and n2
:
>>> n1 = n1 | n2 # 1
>>> n1 |= n2 # 2
where n1
is equivalent via:
- an assigned bitwise OR operation
- an in-place bitwise OR operation
Example
Here we apply bitwise OR (|
) and the in-place bitwise OR (|=
) to numbers:
>>> n1 = 0
>>> n2 = 1
>>> # Bitwise OR, |
>>> n1 | n2
1
>>> n1
0
>>> # In-place Bitwise OR, |=
>>> n1 |= n2
>>> n1
1
Review
This section briefly reviews some bitwise math. In the simplest case, the bitwise OR operation compares two binary bits. It will always return 1
except when both bits are 0
.
>>> assert 1 == (1 | 1) == (1 | 0) == (0 | 1)
>>> assert 0 == (0 | 0)
We now extend this idea beyond binary numbers. Given any two integral numbers (lacking fractional components), we apply the bitwise OR and get an integral result:
>>> a = 10
>>> b = 16
>>> a | b
26
How? In general, the bitwise operations follow some "rules":
- internally compare binary equivalents
- apply the operation
- return the result as the given type
Let's apply these rules to our regular integers above.
(1) Compare binary equivalents, seen here as strings (0b
denotes binary):
>>> bin(a)
'0b1010'
>>> bin(b)
'0b10000'
(2) Apply a bitwise OR operation to each column (0
when both are 0
, else 1
):
01010
10000
-----
11010
(3) Return the result in the given type, e.g. base 10, decimal:
>>> int(0b11010)
26
The internal binary comparison means we can apply the latter to integers in any base, e.g. hex and octal:
>>> a = 10 # 10, dec
>>> b = 0b10000 # 16, bin
>>> c = 0xa # 10, hex
>>> d = 0o20 # 16, oct
>>> a | b
26
>>> c | d
26
See Also
- An example of overloading the
__ior__()
method to iterate iterables in aMutableSet
abstract base class - R. Hettinger's OrderedSet recipe (see lines 3 and 10 respectively)
- A thread on Python-ideas on why to use
|=
to update a set - A section B.8 of Dive in Python 3 on special methods of Python operators
- In-place binary operators fallback to regular methods, see cpython source code (eval.c and abstract.c). Thanks @asotille.
+The in-place bitwise OR operator cannot be applied to literals; assign objects to names.
++Special methods return the same operations as their corresponding operators.
This is just an OR operation between the current variable and the other one. Being T=True
and F=False
, see the output graphically:
r | s | r|=s |
---|---|---|
T | T | T |
T | F | T |
F | T | T |
F | F | F |
For example:
>>> r=True
>>> r|=False
>>> r
True
>>> r=False
>>> r|=False
>>> r
False
>>> r|=True
>>> r
True
When used with sets it performs union operation.