Общий ответ таков: +=
пытается вызвать специальный метод __iadd__
, а если он недоступен, он пытается использовать __add__
. Таким образом, проблема заключается в разнице между этими специальными методами.
Специальный метод __iadd__
предназначен для сложения на месте, то есть он мутирует объект, на который воздействует. Специальный метод __add__
возвращает новый объект и также используется для стандартного оператора +
.
Таким образом, когда оператор +=
используется для объекта, для которого определен __iadd__
, объект изменяется на месте. В противном случае он вместо этого попытается использовать обычный __add__
и вернуть новый объект.
Именно поэтому для изменяемых типов, таких как списки, +=
изменяет значение объекта, тогда как для неизменяемых типов, таких как кортежи, строки и целые числа, вместо этого возвращается новый объект (a += b
становится эквивалентным a = a + b
).
Для типов, которые поддерживают как __iadd__
, так и __add__
, вы должны быть осторожны с тем, какой из них вы используете. a += b
будет вызывать __iadd__
и изменять a
, тогда как a = a + b
создаст новый объект и присвоит ему a
. Они не одна и та же операция!
>>> a1 = a2 = [1, 2]
>>> b1 = b2 = [1, 2]
>>> a1 += [3] # Uses __iadd__, modifies a1 in-place
>>> b1 = b1 + [3] # Uses __add__, creates new list, assigns it to b1
>>> a2
[1, 2, 3] # a1 and a2 are still the same list
>>> b2
[1, 2] # whereas only b1 was changed
Для неизменяемых типов (где у вас нет __iadd__
) a += b
и a = a + b
эквивалентны. Это то, что позволяет вам использовать +=
для неизменяемых типов, что может показаться странным дизайнерским решением, пока вы не решите, что иначе вы не сможете использовать +=
для неизменяемых типов, таких как числа!