Aaron Lelevier
Aaron Lelevier

Reputation: 20760

Python a &= b meaning?

What does the &= operator mean in Python, and can you give me a working example?

I am trying to understand the __iand__ operator.

I just don't know what &= means and have looked online but couldn't find it.

Upvotes: 43

Views: 84292

Answers (7)

itachi
itachi

Reputation: 3597

Disclaimer: I focused on __and__ and __iand__, but the below description works exactly the same for any other combination of operators and their in place counterparts.

tl;dr

& / __and__ &= / __iand__
Side effects (e.g. mutation of one of the arguments) Virtually possible, however discouraged Highly expected
Result New instance Same instance

Longer answer

What's important to understand is that __and__ function is supposed to be pure, that means it shouldn't have side effects, and it must return the same result for the very same arguments.

a = 5
b = 3

print(a & b)
print(a.__and__(b))
print(int.__and__(a, b))

No matter how many times you invoke the function, the result is always 1. Since the function doesn't have side effects, it feels natural to return a value from it because the result must be stored somehow. Besides, a & b is an expression so it's expected to return a result.

On the other side we have __iand__. The i on the beginning stands for in place. For most of the time it is invoked as a consequence of using &= operator which in fact is a so called "augmented assignment" operator, and the keyword here is "assignment". Assignments in Python are statements, that means they do not produce results, as opposed to expressions, but can have side effects. The expected side effect of &= is changing whatever the operator has on its left-hand side.

Take a look at the following code:

a = 5
b = 3

print(a &= b)

If you try it, it won't work because print expects an expression argument. But, you can do this:

print(a.__iand__(b))

However, this will fail too because int doesn't implement __iand__ (understandable, primitives are immutable). If so, why the below code works and outputs 1?

a &= b
print(a)

When Python can't find __iand__, it will expand a &= b to a = a & b, so it in fact invokes a.__and__(b), and overwrites the content of the a variable. A mutable integer type could look like this:

class MutableInt:
    def __init__(self, value):
        self.value = value

    def __and__(self, other):
        return MutableInt(self.value & other.value)

    def __iand__(self, other):
        self.value &= other.value
        return self

    def __str__(self):
        return str(self.value)

Upvotes: 0

Aaron Hall
Aaron Hall

Reputation: 394815

What does a &= b mean?

This depends on the implementation for the types of a and b, but semantics are essentially that

a &= b

is the update of a with its "AND" for b. That "AND" operation could be a set intersection, a binary AND operation, or some other operation that can be implemented by a programmer.

So there are multiple ways of implementing it.

In the following sections, I demonstrate __and__ and __iand__ and give multiple examples of types for which there are different semantics below (set, in which the a object is mutated in-place, and frozenset and int which are immutable, so the variable a is now pointing to the new object).

Explanation

Understandable that you can't find much reference on it. I find it hard to get references on this too, but they exist.

The i in iand means in-place, so it's the in-place operator for &. &= calls the __iand__ operator, if it is implemented. If not implemented, it is the same as x = x & y.

Built-in Example, Sets:

It's primarily used to update the intersection of built-in set types:

>>> a = set('abc') 
>>> a &= set('cbe')
>>> a
set(['c', 'b'])

which is the same as:

>>> a = set('abc')
>>> a.__iand__(set('cbe'))
set(['c', 'b'])

It is very similar to calling the set.intersection_update method, and would be used in control flow as you would do an in-place update of any object or variable (if the object is immutable).

Unimplemented Built-in Example

The less commonly used immutable frozenset object would be replaced in memory on the inplace update, and the variable name would point to the new object in memory.

>>> a = frozenset('abc')
>>> a &= set('bce')
>>> a
frozenset({'c', 'b'})

In this case, since frozenset doesn't implement an __iand__ method,

>>> a = frozenset('abc')
>>> a.__iand__(set('cbe'))

Traceback (most recent call last):
  File "<pyshell#160>", line 1, in <module>
    a = frozenset('abc'); a.__iand__(set('cbe'))
AttributeError: 'frozenset' object has no attribute '__iand__'

it *is (nearly) identical to

a = a & set('bce')

*(I say nearly because if you examine the bytecode, you'll see that the underlying implementation treats sets and frozensets the same, even though frozensets don't have __iand__, and sets do, because each calls INPLACE_AND, at least for compiled functions.)

Built-in Example, Binary Flags:

Similar to Sets, we can use the &= to update the intersection of binary option flags where the value for True is 1. Below, we demonstrate that the "binary AND", (akin to intersection) of the binary numbers 1110 and 1011 is 1010:

>>> option_flags = int('1110', 2)
>>> option_flags
14
>>> option_flags &= int('1011', 2)
>>> option_flags
10
>>> bin(option_flags)
'0b1010'

Since int objects are not mutable, like the frozenset example, this actually only reassigns the variable option_flags to the newly calculated value.

Upvotes: 50

XpressGeek
XpressGeek

Reputation: 3677

It means bitwise AND operation.

Example :

x = 5
x &= 3 #which is similar to x = x & 3
print(x)

Answer : 1

How does it works?

The binary of 5 is : 0 1 0 1
The binary of 3 is : 0 0 1 1

AND operation : (If both sides are 1/True then result is 1/True)

0 1 0 1  #Binary of 5
0 0 1 1  #Binary of 3
---------------------
0 0 0 1  #Binary of 1

So, the answer is 1

Upvotes: 2

Ameer Ul Islam
Ameer Ul Islam

Reputation: 390

to put it in simple terms. Under the hood it does bit-wise binary operation.

for example 5 in binary is 0101 and 3 in binary is 0011

now do "And" operation between them (when both are 1 the result is one, 0 otherwise) and you will get binary 0001 which means 1 in decimal.

x = 5
x &= 3
print(x)
output >>> 1

Upvotes: 4

DSM
DSM

Reputation: 352979

Contrary to some of the other answers, a &= b is not shorthand for a = a & b, though I admit it often behaves similarly for built-in immutable types like integers.

a &= b can call the special method __iand__ if available. To see the difference, let's define a custom class:

class NewIand(object):
    def __init__(self, x):
        self.x = x
    def __and__(self, other):
        return self.x & other.x
    def __iand__(self, other):
        return 99  

After which we have

>>> a = NewIand(1+2+4)
>>> b = NewIand(4+8+16)
>>> a & b
4
>>> a = a & b
>>> a
4

but

>>> a = NewIand(1+2+4)
>>> b = NewIand(4+8+16)
>>> a &= b
>>> a
99

Upvotes: 15

Stefano Sanfilippo
Stefano Sanfilippo

Reputation: 33046

It is a shorthand for:

a = a & b

& is bitwise and (see link for further explanation) if a and b are either int or long.

Otherwise, the statement is equivalent to:

a = a.__iand__(b)

if __iand__ is defined for a.

Upvotes: 8

tayfun
tayfun

Reputation: 3135

It is very similar to +=. It means

a = a & b

Upvotes: 2

Related Questions