Hep duyarız, x += y ifadesi ile x = x + y ifadesi denktir diye. Fakat her zaman değil! Python kodlarken bilmemiz gerekenler var. Alttaki koda bakalım:

import numpy as np

y = np.linspace(0, 1, 10)

x = np.repeat([1], 10)
x += y
print x
# Çıktı: [1 1 1 1 1 1 1 1 1 2]

x = np.repeat([1], 10)
x = x + y
print x
# Çıktı: [ 1.          1.11111111  1.22222222  1.33333333  1.44444444  1.55555556
#          1.66666667  1.77777778  1.88888889  2.        ]

Sonuçlar farklı çıkıyor. Peki, neden? İpucu kullanılan verilerin tiplerinde yatıyor.

x = np.repeat([1], 10)
print x.dtype
# Çıktı: int32
y = np.linspace(0, 1, 10)
print y.dtype
# Çıktı: float64
x += y
print x.dtype
# Çıktı: int32
x = x + y
print x.dtype
# Çıktı: float64

Demek ki x += y ifadesi tam olarak x = x + y gibi çalışmıyor. İlkinde, işlem sonucunda elde edilecek veri tipi atanacak verinin tipiyle aynı oluyor. Yani int32. Fakat x = x + y ifadesi için önce toplam hesaplanıyor ve toplamın sonucunda elde edilen veri tipi baskın çıkıyor. float64 ile int32 tiplerinin toplamı tabii ki float64 baskınlığıyla sonuçlanıyor.

Daha teknik ifade edecek olursak ilk ifade __iadd__ metodunu, ikincisi ise __add__ metodunu çağırıyor. API perspektifinden bakarsak __iadd__ değişebilir (mutable) nesnelerin yerinde (in-place) değişmesi için kullanmak üzere sunulmuştur. __add__ metodu ise yeni bir örnek döndürür. Değişemez (immutable) nesneler içinse her ikisi de yeni örnekler döndürür, farkları dönen örneklerin isimuzayları (namespace) olacaktır. __iadd__ yeni nesneyi özgün nesnenin isimzayına atar.

Başka bir örneğe bakalım:

a = [1, 2, 3]
b = a
b += [1, 2, 3]
print a  
# Çıktı: [1, 2, 3, 1, 2, 3]
print b  
# Çıktı: [1, 2, 3, 1, 2, 3]

ve şununla kıyaslayalım:

a = [1, 2, 3]
b = a
b = b + [1, 2, 3]
print a 
# Çıktı: [1, 2, 3]
print b 
# Çıktı: [1, 2, 3, 1, 2, 3]

Fark etmelisiniz ki ilk örnekte a ve b aynı nesneyi işaret ediyorlar ve sonuç ikisine de yansıyor. Fakat ikinci örnekte b yeni bir nesneye atanıyor, yani işaret ettiği nesne farklılaşıyor ve a bu değişiklikten etkilenmiyor.

Aslında __iadd__ için şuna bakılıyor:

try:
    i = i.__iadd__(1)
except AttributeError:
    i = i.__add__(1)

Dolayısıyla sizin yazacağınız sınıflarda __iadd__ metodunu ayrıca kodlamadıysanız aralarında bir fark olmayacaktır.

Kaynaklar: [1], [2].