Pertanyaan Tambahkan ke daftar yang ditentukan dalam tuple - apakah ini bug? [duplikat]


Pertanyaan ini sudah memiliki jawaban di sini:

Jadi saya punya kode ini:

tup = ([1,2,3],[7,8,9])
tup[0] += (4,5,6)

yang menghasilkan kesalahan ini:

TypeError: 'tuple' object does not support item assignment

Sementara kode ini:

tup = ([1,2,3],[7,8,9])
try:
    tup[0] += (4,5,6)
except TypeError:
    print tup

mencetak ini:

([1, 2, 3, 4, 5, 6], [7, 8, 9])

Apakah perilaku ini diharapkan?

Catatan

Saya menyadari ini bukan kasus penggunaan yang sangat umum. Namun, sementara kesalahan itu diperkirakan, saya tidak mengharapkan perubahan daftar.


32
2018-04-20 11:56


asal


Jawaban:


Ya itu sudah diduga.

Tuple tidak dapat diubah. Sebuah tuple, seperti daftar, adalah struktur yang mengarah ke objek lain. Tidak peduli apa benda-benda itu. Mereka bisa berupa string, angka, tupel, daftar, atau objek lain.

Jadi melakukan apa pun pada salah satu objek yang terdapat di tuple, termasuk menambahkan objek itu jika itu daftar, tidak relevan dengan semantik tuple.

(Bayangkan jika Anda menulis sebuah kelas yang memiliki metode di atasnya yang menyebabkan keadaan internal berubah. Anda tidak akan mengharapkan itu tidak mungkin untuk memanggil metode tersebut pada objek berdasarkan di mana itu disimpan).

Atau contoh lain:

>>> l1 = [1, 2, 3]
>>> l2 = [4, 5, 6]
>>> t = (l1, l2)
>>> l3 = [l1, l2]
>>> l3[1].append(7)

Dua daftar yang bisa berubah yang direferensikan oleh daftar dan oleh sebuah tuple. Haruskah saya dapat melakukan baris terakhir (jawab: ya). Jika Anda berpikir jawabannya tidak, mengapa tidak? Harus t mengubah semantik l3 (jawab: tidak).

Jika Anda menginginkan objek struktur sekuensial yang tidak dapat diubah, itu harus menjadi tuples sepanjang jalan turun.

Mengapa itu salah?

Contoh ini menggunakan operator infiks:

Banyak operasi memiliki versi "di tempat". Fungsi-fungsi berikut   menyediakan akses yang lebih primitif ke operator di tempat daripada biasanya   sintaksis; misalnya, pernyataan x + = y ekuivalen dengan x =   operator.iadd (x, y). Cara lain untuk mengatakannya adalah mengatakan z =   operator.iadd (x, y) setara dengan pernyataan senyawa z = x; z   + = y.

https://docs.python.org/2/library/operator.html

Jadi ini:

l = [1, 2, 3]
tup = (l,)
tup[0] += (4,5,6)

setara dengan ini:

l = [1, 2, 3]
tup = (l,)
x = tup[0]
x = x.__iadd__([4, 5, 6]) # like extend, but returns x instead of None
tup[0] = x

Itu __iadd__ baris berhasil, dan memodifikasi daftar pertama. Jadi daftarnya telah diubah. Itu __iadd__ panggilan mengembalikan daftar yang dimutasikan.

Baris kedua mencoba untuk menetapkan daftar kembali ke tuple, dan ini gagal.

Jadi, pada akhir program, daftar telah diperpanjang tetapi bagian kedua dari += operasi gagal. Untuk spesifiknya, lihat pertanyaan ini.


28
2018-04-20 11:58



Yah, saya kira tup[0] += (4, 5, 6) diterjemahkan ke:

tup[0] = tup[0].__iadd__((4,5,6))

tup[0].__iadd__((4,5,6)) dijalankan biasanya mengubah daftar di elemen pertama. Tetapi penugasan gagal karena tupel tidak dapat diubah.


11
2018-04-20 12:07



Tuples tidak dapat diubah secara langsung, benar. Namun, Anda dapat mengubah elemen tupel dengan referensi. Seperti:

>>> tup = ([1,2,3],[7,8,9])
>>> l = tup[0]
>>> l += (4,5,6)
>>> tup
([1, 2, 3, 4, 5, 6], [7, 8, 9])

5
2018-04-20 12:11



Pengembang Python menulis penjelasan resmi tentang mengapa hal itu terjadi di sini: https://docs.python.org/2/faq/programming.html#why-does-a-tuple-i-item-raise-an-exception-when-the-addition-works

Versi singkatnya adalah bahwa + = sebenarnya melakukan dua hal, satu tepat setelah yang lain:

  1. Jalankan benda di sebelah kanan.
  2. tetapkan hasilnya ke variabel di sebelah kiri

Dalam kasus ini, langkah 1 berfungsi karena Anda diizinkan untuk menambahkan hal-hal ke daftar (mereka tidak dapat diubah), tetapi langkah 2 gagal karena Anda tidak dapat memasukkan barang-barang menjadi tupel setelah membuatnya (tuples tidak dapat diubah).

Dalam program nyata, saya sarankan Anda tidak mencoba kecuali klausa tup[0].extend([4,5,6]) melakukan hal yang sama persis.


1
2017-10-05 11:38