Ketika Anda menulis, [x]*3
Anda mendapatkan, pada dasarnya, daftar [x, x, x]
. Yaitu, daftar dengan 3 referensi yang sama x
. Ketika Anda memodifikasi single ini, x
itu terlihat melalui ketiga referensi untuk itu:
x = [1] * 4
l = [x] * 3
print(f"id(x): {id(x)}")
# id(x): 140560897920048
print(
f"id(l[0]): {id(l[0])}\n"
f"id(l[1]): {id(l[1])}\n"
f"id(l[2]): {id(l[2])}"
)
# id(l[0]): 140560897920048
# id(l[1]): 140560897920048
# id(l[2]): 140560897920048
x[0] = 42
print(f"x: {x}")
# x: [42, 1, 1, 1]
print(f"l: {l}")
# l: [[42, 1, 1, 1], [42, 1, 1, 1], [42, 1, 1, 1]]
Untuk memperbaikinya, Anda perlu memastikan bahwa Anda membuat daftar baru di setiap posisi. Salah satu cara untuk melakukannya adalah
[[1]*4 for _ in range(3)]
yang akan mengevaluasi ulang [1]*4
setiap kali alih-alih mengevaluasi satu kali dan membuat 3 referensi ke 1 daftar.
Anda mungkin bertanya-tanya mengapa *
tidak dapat membuat objek independen seperti pemahaman daftar. Itu karena operator perkalian *
beroperasi pada objek, tanpa melihat ekspresi. Saat Anda menggunakan *
untuk mengalikan [[1] * 4]
dengan 3, *
hanya melihat daftar 1-elemen yang [[1] * 4]
dievaluasi, bukan [[1] * 4
teks ekspresi. *
tidak tahu bagaimana membuat salinan elemen itu, tidak tahu bagaimana cara mengevaluasi kembali [[1] * 4]
, dan tidak tahu Anda bahkan ingin salinan, dan secara umum, bahkan mungkin tidak ada cara untuk menyalin elemen.
Satu-satunya pilihan *
adalah membuat referensi baru ke sublist yang ada alih-alih mencoba membuat sublists baru. Hal lain akan menjadi tidak konsisten atau memerlukan pendesainan ulang besar terhadap keputusan desain bahasa mendasar.
Sebaliknya, pemahaman daftar mengevaluasi kembali ekspresi elemen pada setiap iterasi. [[1] * 4 for n in range(3)]
Mengevaluasi kembali [1] * 4
setiap waktu karena alasan yang sama [x**2 for x in range(3)]
mengevaluasi kembali x**2
setiap waktu. Setiap evaluasi [1] * 4
menghasilkan daftar baru, sehingga pemahaman daftar melakukan apa yang Anda inginkan.
Kebetulan, [1] * 4
juga tidak menyalin elemen [1]
, tapi itu tidak masalah, karena bilangan bulat tidak dapat diubah. Anda tidak dapat melakukan sesuatu seperti 1.value = 2
dan mengubah angka 1 menjadi angka 2.
[x]*3
menyimpan 3 referensi suka[x, x, x]
hanya benar bilax
bisa berubah. Ini tidak berfungsi untuk misalnyaa=[4]*3
, di mana setelaha[0]=5
,a=[5,4,4].
[4]*3
pada dasarnya setara denganx = 4; [x, x, x]
. Memang benar, bahwa ini tidak akan pernah menyebabkan masalah karena4
tidak dapat diubah. Juga, contoh Anda yang lain sebenarnya bukan kasus yang berbeda.a = [x]*3; a[0] = 5
tidak akan menyebabkan masalah bahkan jikax
bisa diubah, karena Anda tidak mengubahx
, hanya memodifikasia
. Saya tidak akan menggambarkan jawaban saya sebagai menyesatkan atau salah - Anda tidak bisa menembak diri sendiri jika Anda berurusan dengan benda yang tidak dapat diubah.x = 1000; lst = [x]*2; lst[0] is lst[1]
->True
. Python tidak membedakan antara objek yang dapat berubah dan tidak berubah di sini.Visualisasikan Python Tutor Langsung
sumber
x
merujuk. Jika Anda membuat objek global yang unik denganx = object()
dan kemudian membuatmatrix = [[x] * 2]
ini menjadi kenyataan:matrix[0][0] is matrix[0][1]
list
) jadi jika arow = [x] * 2
daripada dimatrix = [row] * 2
mana kedua baris adalah objek yang persis sama, dan sekarang perubahan ke satu barismatrix[0][0] = y
tiba-tiba mencerminkan di yang lain(matrix[0][0] is matrix[1][0]) == True
Sebenarnya, inilah yang Anda harapkan. Mari kita uraikan apa yang terjadi di sini:
Anda menulis
Ini setara dengan:
Ini berarti
lst
daftar dengan 3 elemen yang semuanya menunjuk kelst1
. Ini berarti dua baris berikut ini setara:Seperti
lst[0]
tidak lain adalahlst1
.Untuk mendapatkan perilaku yang diinginkan, Anda dapat menggunakan pemahaman daftar:
Dalam hal ini, ekspresi dievaluasi ulang untuk setiap n, yang mengarah ke daftar yang berbeda.
sumber
id(lst[0][0])
danid(lst[1][0])
atau bahkanid(lst[0])
danid(lst[1])
atau bahkan:
Membuat daftar yang mereferensikan internal
[1,1,1,1]
3 kali - bukan tiga salinan daftar batin, jadi setiap kali Anda memodifikasi daftar (di posisi apa pun), Anda akan melihat perubahannya tiga kali.Sama dengan contoh ini:
di mana itu mungkin sedikit kurang mengejutkan.
sumber
Di samping jawaban yang diterima yang menjelaskan masalah dengan benar, dalam pemahaman daftar Anda, jika Anda menggunakan penggunaan python-2.x
xrange()
yang mengembalikan generator yang lebih efisien (range()
dengan python 3 melakukan pekerjaan yang sama)_
daripada variabel yang dibuangn
:Juga, sebagai cara yang jauh lebih Pythonic yang dapat Anda gunakan
itertools.repeat()
untuk membuat objek iterator elemen berulang:PS Menggunakan numpy, jika Anda hanya ingin membuat array yang atau nol Anda dapat menggunakan
np.ones
dannp.zeros
dan / atau untuk penggunaan nomor lainnyanp.repeat()
:sumber
Wadah python berisi referensi ke objek lain. Lihat contoh ini:
Dalam hal ini
b
adalah daftar yang berisi satu item yang merupakan referensi ke daftara
. Daftara
ini bisa berubah.Penggandaan daftar oleh integer setara dengan menambahkan daftar ke dirinya sendiri beberapa kali (lihat operasi urutan umum ). Jadi, lanjutkan dengan contoh:
Kita dapat melihat bahwa daftar
c
sekarang berisi dua referensi ke daftara
yang setara denganc = b * 2
.FAQ Python juga berisi penjelasan tentang perilaku ini: Bagaimana cara membuat daftar multidimensi?
sumber
myList = [[1]*4] * 3
membuat satu objek daftar[1,1,1,1]
dalam memori dan menyalin rujukannya 3 kali lipat. Ini setara denganobj = [1,1,1,1]; myList = [obj]*3
. Setiap modifikasiobj
akan tercermin di tiga tempat, di mana punobj
direferensikan dalam daftar. Pernyataan yang benar adalah:atau
Hal penting yang perlu diperhatikan di sini adalah bahwa
*
operator sebagian besar digunakan untuk membuat daftar literal . Meskipun1
tidak berubah,obj =[1]*4
masih akan membuat daftar1
berulang 4 kali lipat untuk membentuk[1,1,1,1]
. Tetapi jika ada referensi ke objek yang tidak dapat diubah dibuat, objek tersebut akan ditimpa dengan yang baru.Ini berarti jika kita melakukannya
obj[1]=42
, makaobj
akan menjadi[1,42,1,1]
tidakseperti anggapan beberapa orang. Ini juga dapat diverifikasi:[42,42,42,42]
sumber
obj[2] = 42
menggantikan referensi pada indeks2
, sebagai lawan untuk bermutasi objek yang dirujuk oleh indeks itu, yang adalah apamyList[2][0] = ...
(myList[2]
adalah daftar, dan tugas mengubah referensi pada indeks 0 dalam daftar tha). Tentu saja, bilangan bulat tidak bisa berubah, tapi banyak jenis objek yang . Dan perhatikan bahwa[....]
notasi tampilan daftar juga merupakan bentuk sintaks literal! Jangan bingung antara senyawa (seperti daftar) dan objek skalar (seperti bilangan bulat), dengan objek yang dapat berubah vs yang tidak dapat diubah.Dengan kata-kata sederhana ini terjadi karena dalam python semuanya bekerja dengan referensi , jadi ketika Anda membuat daftar daftar seperti itu pada dasarnya Anda berakhir dengan masalah seperti itu.
Untuk mengatasi masalah Anda, Anda dapat melakukan salah satu di antaranya: 1. Gunakan dokumentasi array numpy untuk numpy.empty 2. Tambahkan daftar saat Anda membuka daftar. 3. Anda juga dapat menggunakan kamus jika Anda mau
sumber
Biarkan kami menulis ulang kode Anda dengan cara berikut:
Kemudian setelah ini, jalankan kode berikut untuk membuat semuanya lebih jelas. Apa yang dilakukan kode pada dasarnya adalah mencetak
id
objek yang diperoleh, yangdan akan membantu kami mengidentifikasi mereka dan menganalisis apa yang terjadi:
Dan Anda akan mendapatkan output berikut:
Jadi sekarang mari kita selangkah demi selangkah. Anda memiliki
x
yang mana1
, dan satu daftar elemen yangy
berisix
. Langkah pertama Anda adalahy * 4
yang akan memberi Anda daftar baruz
, yang pada dasarnya[x, x, x, x]
, yaitu membuat daftar baru yang akan memiliki 4 elemen, yang merupakan referensi kex
objek awal . Langkah bersih sangat mirip. Pada dasarnya Anda lakukanz * 3
, yaitu[[x, x, x, x]] * 3
dan kembali[[x, x, x, x], [x, x, x, x], [x, x, x, x]]
, untuk alasan yang sama seperti untuk langkah pertama.sumber
Saya kira semua orang menjelaskan apa yang terjadi. Saya menyarankan satu cara untuk menyelesaikannya:
myList = [[1 for i in range(4)] for j in range(3)]
print myList
Dan kemudian Anda memiliki:
sumber
Mencoba menjelaskannya dengan lebih deskriptif,
Operasi 1:
Operasi 2:
Melihat mengapa tidak memodifikasi elemen pertama dari daftar pertama tidak mengubah elemen kedua dari setiap daftar? Itu karena
[0] * 2
benar-benar daftar dua angka, dan referensi ke 0 tidak dapat dimodifikasi.Jika Anda ingin membuat salinan klon, coba Operasi 3:
cara lain yang menarik untuk membuat salinan klon, Operasi 4:
sumber
@spelchekr dari multiplikasi daftar Python: [[...]] * 3 membuat 3 daftar yang saling mirror ketika dimodifikasi dan saya memiliki pertanyaan yang sama tentang "Mengapa hanya bagian luar * 3 yang membuat lebih banyak referensi sedangkan yang dalam tidak ? Kenapa tidak semuanya? "
Berikut ini penjelasan saya setelah mencoba kode di atas:
*3
juga membuat referensi, tetapi referensi itu tidak berubah, seperti[&0, &0, &0]
, lalu kapan harus berubahli[0]
, Anda tidak dapat mengubah referensi mendasar dari const int0
, jadi Anda bisa mengubah alamat referensi menjadi yang baru&1
;ma=[&li, &li, &li]
danli
bisa berubah, jadi ketika Anda meneleponma[0][0]=1
, ma [0] [0] sama dengan&li[0]
, jadi semua&li
instance akan mengubah alamat 1 menjadi&1
.sumber
Dengan menggunakan fungsi daftar inbuilt yang dapat Anda lakukan seperti ini
sumber
a.insert(0,[5,1,1,1])