Original Source Here

# The “+=” May Not Do What You Expected In Python

## A discussion and comparison of 3 different “add” functions

Do you think `a = a + b`

is always the same as `a += b`

in Python? The answer is no. Like most of the other Python developers, I was thinking the same thing before, until I realised that there are 3 different “adding” functions in Python.

`__add__()`

`__radd__()`

`__iadd__()`

Don’t recognise them? Don’t worry. If you know that the functions surrounded by double underscores are “magic” functions, you should already realise that these functions should be called in more intuitive ways. Exactly, when we have `a + b`

, the function `a.__add__(b)`

is called behind the scene.

In this article, I’ll introduce all these 3 functions and the differences between them. More importantly, I’ll show you in which scenario `a = a + b`

will not be the same as `a += b`

, and why.

# 1. When __add__ is Called?

As mentioned in the introduction, the function `__add__()`

is called whenever we are doing the SUM calculations. Therefore, we can say that the two expressions below are always the same.

`a + b`

a.__add__(b)

We can verify this by defining a custom class. Let’s call it class “X”. Then, the class “X” will take a number for initialisation. Also, we need to define the magic function `__add__()`

. For convenience, the `__repr__()`

function is also defined so that we can easily print the results.

class X:

def __init__(self, num):

self.num = numdef __add__(self, other_X):

print('I am called!')

return X(self.num + other_X.num)def __repr__(self):

return str(self.num)

Now, let’s instantiate two numbers from this class “X”, and perform a sum calculation.

a = X(1)

b = X(2)print('a + b =', a + b)

In the `__add__()`

function, I’ve added a print function to print something. Then, when we run `a + b`

, it was printed. So, it proved that we have successfully overwritten the `+`

operator for this customised class.

# 2. What Makes __radd__ Different from __add__?

This function is a bit special. It will reverse the order of the two components and then add them. Well, we all know that `a + b = b + a`

if both of them are scalar. Therefore, it will be much easier to demonstrate it using dividing rather than adding.

Fortunately, there are two corresponding functions `__truediv__()`

and `__rturediv()`

. See the examples below.

x = 10

y = 2x.__truediv__(y)

x.__rtruediv__(y)

Therefore,

`x.__truediv__(y) = x/y`

`x.__rtruediv__(y) = y/x`

Similarly,

`x.__add__(y) = x + y`

`x.__radd__(y) = y + x`

If you’re interested in how many other operators or calculations having these features, here is a link to the official docs for you.

# 3. When __radd__ is Called?

Now we understand that the expression `a.__radd__(b)`

is equivalent to `b + a`

. But hold on, when we have the expression `b + a`

, it surely will run `b.__add__(a)`

rather than the former. Then, when the function`__radd__()`

will be called?

Remember we have defined a customised class “X” and we have to implement the `__add__()`

function to enable the operator “+” to be used between the “X” instances? That means, sometimes we may not have it in a class.

Let’s define another class “Y” and make it identical with “X” but without overwriting the function `__add__()`

.

class Y:

def __init__(self, num):

self.num = numdef __repr__(self):

return str(self.num)

Now, we define `a`

and `b`

again but using X and Y respectively. Then, we try to run `b + a`

.

a = X(1)

b = Y(2)print('b + a =', b + a)

Because `b`

is a type of Y but there is no `__add__()`

implemented, there is no `b.__add__(a)`

existing.

I hope so far you still understand. Then, let’s implement the `__radd__()`

function in class X and then run it again.

In class X, add the following function:

`def __radd__(self, other_num):`

print('Reversed adding!')

return X(self.num + other_num.num)

Yes, your idea is correct. When an object does not have `__add__()`

but the other object in the sum calculation have the `__radd__()`

function implemented, this function will be called.

# 4. When __iadd__ is Called?

The shore answer is that the `__iadd__()`

function will be called when we use the `+=`

operator. However, unlike the `__radd__()`

function, even if we don’t implement the `__iadd__()`

function, the “+=” operator can still work. It will just fall back to the `__add__()`

function if there is one.

Let keep using the example defined in section 1.

class X:

def __init__(self, num):

self.num = numdef __add__(self, other_X):

print('I am called!')

return X(self.num + other_X.num)def __repr__(self):

return str(self.num)a = X(1)

b = X(2)a += b

print('new a =', a)

Of course, if we have implemented the function `__iadd__()`

and use the “+=” operator, it will be called.

Let’s prove that by implementing the `__iadd__()`

function.

class X:

def __init__(self, num):

self.num = numdef __add__(self, other_X):

print('I am called!')

return X(self.num + other_X.num)def __iadd__(self, other_X):

print('return myself')

self.num = self.num + other_X.num

return selfdef __repr__(self):

return str(self.num)

# 5. The Difference Between “a=a+b” and “a+=b”

In the previous example, have you noticed that the difference in how I implement the `__iadd__()`

function? Yes, rather than returning a new object as `__add__()`

do, I make it return `self`

. In other words, no new object is created in this operation.

This is also the official definition of `__iadd__()`

.

`object.`

(__iadd__self,other)These methods should attempt to do the operation

in-place (modifying self) and return the result.

That will create the difference between the functions `__add__()`

and `__iadd__()`

, and consequently makes the operator “+” and “+=” different in some scenarios.

Don’t forget that we can use the “+” operator on any Python list, so let’s experiment on lists.

Firstly, let’s define two simple lists `a`

and `b`

.

`a = [1, 2]`

b = [3, 4]

Then, let’s define another variable `c`

, assign `a`

to it.

`c = a`

Can’t be simpler, right? 🙂

Now, let’s add `a`

and `b`

and assign it back to `a`

.

`a = a + b`

print("a =", a)

print("c =", c)

The result shows that `a`

and `c`

are different. The list `c`

still equals to the “original” `a`

.

Now, let’s try the “+=” operator.

a = [1, 2]

b = [3, 4]

c = aa += b

print("a =", a)

print("c =", c)

This time, the object `c`

was changed because of `a`

was modified!

This is exactly because of the feature of the function `__iadd__()`

. It will modify the object in place rather than a new one, and this leads to the modification of the object `c`

.

In fact, only **mutable** objects will be affected by this “feature”. If the objects we’re trying to sum are **immutable**, the “+” and “+=” can be considered as the same.

We can experiment on tuples because Python tuples are considered **immutable** objects.

a = (1, 2)

b = (3, 4)

c = aa += b

print("a", a)

print("c", c)

This time, the object `c`

wasn’t changed.

Perhaps one more example for integer, because we may always think the “+” and “+=” are the same is caused by we are more often adding numbers rather than lists. For example, integers in Python are immutable as well.

Therefore, it would be safe enough to use “+=” on numbers, don’t worry 🙂

# Summary

In this article, I have introduced the three ways of adding objects in Python. They are “magic functions” which will be called when we use the operators. Now you should understand when they will be called and what features they will provide.

Most importantly, be aware of the difference between `a = a + b`

and `a += b`

. They will create different results when the objects are **mutable**.

Life is short, use Python!

AI/ML

Trending AI/ML Article Identified & Digested via Granola by Ramsey Elbasheer; a Machine-Driven RSS Bot