Catatan: menggunakan operasi di tempat pada array NumPy yang berbagi memori tidak lagi menjadi masalah di versi 1.13.0 dan seterusnya (lihat detailnya di sini ). Kedua operasi tersebut akan menghasilkan hasil yang sama. Jawaban ini hanya berlaku untuk versi NumPy sebelumnya.
Mutasi array saat digunakan dalam komputasi dapat menyebabkan hasil yang tidak terduga!
Dalam contoh dalam pertanyaan, pengurangan dengan -=
memodifikasi elemen kedua a
dan kemudian segera menggunakan elemen kedua yang dimodifikasi itu dalam operasi pada elemen ketiga a
.
Inilah yang terjadi dengan a[1:] -= a[:-1]
langkah demi langkah:
a
adalah larik dengan data [1, 2, 3]
.
Kami memiliki dua pandangan ke data ini: a[1:]
is [2, 3]
, dan a[:-1]
is [1, 2]
.
Pengurangan di tempat -=
dimulai. Elemen pertama a[:-1]
, 1, dikurangi dari elemen pertama a[1:]
. Ini telah diubah a
menjadi [1, 1, 3]
. Sekarang kita memiliki a[1:]
tampilan data [1, 3]
, dan a[:-1]
tampilan data [1, 1]
(elemen kedua dari array a
telah diubah).
a[:-1]
sekarang [1, 1]
dan NumPy sekarang harus mengurangi elemen keduanya yaitu 1 (bukan 2 lagi!) dari elemen kedua a[1:]
. Ini membuat a[1:]
pandangan tentang nilai-nilai [1, 2]
.
a
sekarang menjadi array dengan nilai [1, 1, 2]
.
b[1:] = b[1:] - b[:-1]
tidak memiliki masalah ini karena b[1:] - b[:-1]
membuat larik baru terlebih dahulu dan kemudian menetapkan nilai dalam larik ini ke b[1:]
. Itu tidak mengubah b
dirinya sendiri selama pengurangan, jadi tampilan b[1:]
dan b[:-1]
tidak berubah.
Saran umumnya adalah menghindari memodifikasi satu tampilan dengan yang lain jika tumpang tindih. Ini termasuk operator -=
, *=
dll. Dan menggunakan out
parameter dalam fungsi universal (seperti np.subtract
dan np.multiply
) untuk menulis kembali ke salah satu array.
Secara internal, perbedaannya adalah ini:
a[1:] -= a[:-1]
setara dengan ini:
a[1:] = a[1:].__isub__(a[:-1]) a.__setitem__(slice(1, None, None), a.__getitem__(slice(1, None, None)).__isub__(a.__getitem__(slice(1, None, None)))
sementara ini:
b[1:] = b[1:] - b[:-1]
memetakan ke ini:
b[1:] = b[1:].__sub__(b[:-1]) b.__setitem__(slice(1, None, None), b.__getitem__(slice(1, None, None)).__sub__(b.__getitem__(slice(1, None, None)))
Dalam beberapa kasus,
__sub__()
dan__isub__()
bekerja dengan cara yang serupa. Tapi objek yang bisa berubah harus bermutasi dan mengembalikan dirinya sendiri saat digunakan__isub__()
, sementara mereka harus mengembalikan objek baru dengan__sub__()
.Menerapkan operasi slice pada objek numpy akan membuat tampilan padanya, jadi menggunakannya secara langsung mengakses memori objek "asli".
sumber
Dokumen tersebut mengatakan:
Sebagai aturan umum, augmented substraction (
x-=y
) adalahx.__isub__(y)
, untuk operasi DI- tempat JIKA memungkinkan, jika substraksi normal (x = x-y
) adalahx=x.__sub__(y)
. Pada objek yang tidak bisa berubah seperti integer, itu setara. Tetapi untuk yang bisa berubah seperti array atau daftar, seperti dalam contoh Anda, mereka bisa menjadi hal yang sangat berbeda.sumber