Overriding "+=" in Python? (__iadd__() method)
Is it possible to override += in Python?
Yes, override the __iadd__
method. Example:
def __iadd__(self, other):
self.number += other.number
return self
In addition to what's correctly given in answers above, it is worth explicitly clarifying that when __iadd__
is overriden, the x += y
operation does NOT end with the end of __iadd__
method.
Instead, it ends with x = x.__iadd__(y)
. In other words, Python assigns the return value of your __iadd__
implementation to the object you're "adding to", AFTER the implementation completes.
This means it is possible to mutate the left side of the x += y
operation so that the final implicit step fails. Consider what can happen when you are adding to something that's within a list:
>>> x[1] += y # x has two items
Now, if your __iadd__
implementation (a method of an object at x[1]
) erroneously or on purpose removes the first item (x[0]
) from the beginning of the list, Python will then run your __iadd__
method) & try to assign its return value to x[1]
. Which will no longer exist (it will be at x[0]
), resulting in an ÌndexError
.
Or, if your __iadd__
inserts something to beginning of x
of the above example, your object will be at x[2]
, not x[1]
, and whatever was earlier at x[0]
will now be at x[1]
and be assigned the return value of the __iadd__
invocation.
Unless one understands what's happening, resulting bugs can be a nightmare to fix.
In addition to overloading __iadd__
(remember to return self!), you can also fallback on __add__
, as x += y will work like x = x + y. (This is one of the pitfalls of the += operator.)
>>> class A(object):
... def __init__(self, x):
... self.x = x
... def __add__(self, other):
... return A(self.x + other.x)
>>> a = A(42)
>>> b = A(3)
>>> print a.x, b.x
42 3
>>> old_id = id(a)
>>> a += b
>>> print a.x
45
>>> print old_id == id(a)
False
It even trips up experts:
class Resource(object):
class_counter = 0
def __init__(self):
self.id = self.class_counter
self.class_counter += 1
x = Resource()
y = Resource()
What values do you expect x.id
, y.id
, and Resource.class_counter
to have?