Общий ответ заключается в том, что он +=пытается вызвать __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эквивалентны. Это то, что позволяет вам использовать +=для неизменяемых типов, что может показаться странным дизайнерским решением, пока вы не решите, что в противном случае вы не могли бы использовать +=для неизменных типов, таких как числа!