Bagaimana cara saya menginisialisasi kamus daftar kosong dengan Python?

89

Upaya saya untuk membuat kamus daftar secara terprogram gagal mengizinkan saya untuk menangani kunci kamus satu per satu. Setiap kali saya membuat kamus daftar dan mencoba menambahkan ke satu kunci, semuanya diperbarui. Berikut kasus uji yang sangat sederhana:

data = {}
data = data.fromkeys(range(2),[])
data[1].append('hello')
print data

Hasil sebenarnya: {0: ['hello'], 1: ['hello']}

Hasil yang diharapkan: {0: [], 1: ['hello']}

Inilah yang berhasil

data = {0:[],1:[]}
data[1].append('hello')
print data

Hasil Aktual dan yang Diharapkan: {0: [], 1: ['hello']}

Mengapa fromkeysmetode ini tidak berfungsi seperti yang diharapkan?

Martin Burch
sumber

Jawaban:

112

Meneruskan []sebagai argumen kedua untuk dict.fromkeys()memberikan hasil yang tidak berguna - semua nilai dalam kamus akan menjadi objek daftar yang sama .

Di Python 2.7 atau yang lebih baru, Anda dapat menggunakan pemahaman kamus sebagai gantinya:

data = {k: [] for k in range(2)}

Di versi Python sebelumnya, Anda dapat menggunakan

data = dict((k, []) for k in range(2))
Sven Marnach
sumber
3
Ini adalah perilaku yang agak tidak intuitif, ada ide tentang mengapa objek yang sama digunakan untuk semua kunci?
Bar
3
@Bar Karena tidak ada fungsi lain yang bisa dilakukan dalam semantik bahasa Python. Anda mengirimkan satu objek untuk digunakan sebagai nilai untuk semua kunci, sehingga objek tunggal digunakan untuk semua kunci. Akan lebih baik jika fromkeys()metode menerima fungsi pabrik, jadi kita bisa masuk listsebagai fungsi, dan fungsi itu akan dipanggil sekali untuk setiap kunci yang dibuat, tapi itu bukan API sebenarnya dict.fromkeys().
Sven Marnach
3
Ini sama sekali tidak intuitif. Aku butuh waktu satu jam untuk menemukannya. Terima kasih
Astrid
1
Hal yang sama terjadi jika Anda meneruskan dict () sebagai argumen kedua. Perilaku yang sangat membingungkan.
Orly
@Orly Ini karena kamus kosong pertama dibuat, lalu referensi ke kamus itu diteruskan ke semua inisialisasi.
Dr_Zaszuś
83

Gunakan defaultdict sebagai gantinya:

from collections import defaultdict
data = defaultdict(list)
data[1].append('hello')

Dengan cara ini Anda tidak perlu menginisialisasi semua kunci yang ingin Anda gunakan untuk membuat daftar sebelumnya.

Apa yang terjadi dalam contoh Anda adalah Anda menggunakan satu daftar (bisa berubah):

alist = [1]
data = dict.fromkeys(range(2), alist)
alist.append(2)
print data

akan menghasilkan {0: [1, 2], 1: [1, 2]}.

Martijn Pieters
sumber
2
Dalam kasus saya, saya perlu menginisialisasi semua kunci terlebih dahulu sehingga logika program lainnya dapat berfungsi seperti yang diharapkan, tetapi ini akan menjadi solusi yang baik jika tidak. Terima kasih.
Martin Burch
Saya kira apa yang hilang dari jawaban ini adalah mengatakan bahwa solusi ini berfungsi, sebagai lawan dari OP, karena di listsini bukan daftar kosong, tetapi tipe (atau Anda dapat melihatnya sebagai konstruktor yang dapat dipanggil, saya kira). Jadi setiap kali kunci yang hilang dilewatkan, daftar baru dibuat alih-alih menggunakan kembali yang sama.
Dr_Zaszuś
8

Anda mengisi kamus Anda dengan referensi ke satu daftar sehingga saat Anda memperbaruinya, pembaruan akan tercermin di semua referensi. Cobalah pemahaman kamus sebagai gantinya. Lihat Membuat kamus dengan pemahaman daftar dengan Python

d = {k : v for k in blah blah blah}
cobie
sumber
saran bagus untuk menginisialisasi nilai kamus ... terima kasih cobie! Saya memperluas contoh Anda untuk mengatur ulang nilai-nilai dalam kamus yang ada, d. Saya melakukan ini sebagai berikut: d = {k: 0 untuk k in d}
Yohanes
Apa vjawaban ini?
Dr_Zaszuś
-3

Anda bisa menggunakan ini:

data[:1] = ['hello']
Conner Dassen
sumber
2
Mungkin membantu OP untuk menjelaskan mengapa ini berhasil. Pertanyaan asli diposting aks mengapa tidak berfungsi seperti yang diharapkan.
william.taylor. 09
@ william.taylor.09 agak jelas mengapa ini berhasil, bukan?
Conner Dassen
OP bertanya "Mengapa metode fromkeys tidak berfungsi seperti yang diharapkan?"
william.taylor.09