Memahami dict.copy () - dangkal atau dalam?


429

Saat membaca dokumentasi untuk dict.copy(), dikatakan bahwa itu membuat salinan kamus yang dangkal. Hal yang sama berlaku untuk buku yang saya ikuti (Referensi Python Beazley), yang mengatakan:

Metode m.copy () membuat salinan dangkal dari item yang terkandung dalam objek pemetaan dan menempatkannya dalam objek pemetaan baru.

Pertimbangkan ini:

>>> original = dict(a=1, b=2)
>>> new = original.copy()
>>> new.update({'c': 3})
>>> original
{'a': 1, 'b': 2}
>>> new
{'a': 1, 'c': 3, 'b': 2}

Jadi saya berasumsi ini akan memperbarui nilai original(dan menambahkan 'c': 3) juga karena saya sedang melakukan salinan yang dangkal. Seperti jika Anda melakukannya untuk daftar:

>>> original = [1, 2, 3]
>>> new = original
>>> new.append(4)
>>> new, original
([1, 2, 3, 4], [1, 2, 3, 4])

Ini berfungsi seperti yang diharapkan.

Karena keduanya adalah salinan dangkal, mengapa itu dict.copy()tidak berfungsi seperti yang saya harapkan? Atau pemahaman saya tentang menyalin dangkal vs dalam cacat?


2
Pelik bahwa mereka tidak menjelaskan "dangkal". Pengetahuan orang dalam, berkedip. Hanya dict dan kunci yang merupakan salinan sementara dict yang bersarang di dalam level pertama tersebut adalah referensi, tidak dapat dihapus dalam satu lingkaran misalnya. Jadi dict.copy Python () dalam hal ini tidak berguna atau intuitif. Terima kasih atas pertanyaan anda
gseattle

Jawaban:


991

Dengan "menyalin dangkal" itu berarti konten kamus tidak disalin oleh nilai, tetapi hanya membuat referensi baru.

>>> a = {1: [1,2,3]}
>>> b = a.copy()
>>> a, b
({1: [1, 2, 3]}, {1: [1, 2, 3]})
>>> a[1].append(4)
>>> a, b
({1: [1, 2, 3, 4]}, {1: [1, 2, 3, 4]})

Sebaliknya, salinan yang dalam akan menyalin semua konten berdasarkan nilai.

>>> import copy
>>> c = copy.deepcopy(a)
>>> a, c
({1: [1, 2, 3, 4]}, {1: [1, 2, 3, 4]})
>>> a[1].append(5)
>>> a, c
({1: [1, 2, 3, 4, 5]}, {1: [1, 2, 3, 4]})

Begitu:

  1. b = a: Tugas referensi, Buat adan barahkan ke objek yang sama.

    Ilustrasi 'a = b': 'a' dan 'b' keduanya menunjuk ke '{1: L}', 'L' menunjuk ke '[1, 2, 3]'.

  2. b = a.copy(): Menyalin dangkal, adan bakan menjadi dua objek yang terisolasi, tetapi isinya masih memiliki referensi yang sama

    Ilustrasi 'b = a.copy ()': 'a' menunjuk ke '{1: L}', 'b' menunjuk ke '{1: M}', 'L' dan 'M' keduanya menunjuk ke '[ 1, 2, 3] '.

  3. b = copy.deepcopy(a): Penyalinan dalam, adan bstruktur dan konten menjadi sepenuhnya terisolasi.

    Ilustrasi 'b = copy.deepcopy (a)': 'a' menunjuk ke '{1: L}', 'L' menunjuk ke '[1, 2, 3]';  'b' menunjuk ke '{1: M}', 'M' menunjuk ke contoh berbeda dari '[1, 2, 3]'.


Jawaban yang bagus, tetapi Anda mungkin mempertimbangkan untuk memperbaiki kesalahan tata bahasa dalam kalimat pertama Anda. Dan tidak ada alasan untuk tidak menggunakannya Llagi b. Melakukannya akan menyederhanakan contoh.
Tom Russell

@kennytm: Apa perbedaan antara dua contoh pertama, sebenarnya? Anda sampai di sana hasil yang sama, tetapi implementasi batin yang sedikit berbeda, tetapi untuk apa masalahnya?
JavaSa

@ TomRussell: Atau siapa pun, karena pertanyaan ini sudah cukup lama, pertanyaan klarifikasi saya adalah untuk semua orang
JavaSa

@ JavaSa Itu penting jika, katakanlah, Anda lakukan b[1][0] = 5. Jika bmerupakan salinan dangkal, Anda baru saja berubah a[1][0].
Tom Russell

2
Penjelasan hebat, ... benar-benar menyelamatkan hariku! Terima kasih ... Bisakah ini diterapkan pada daftar, str, dan tipe data python lainnya?
Bhuro

38

Ini bukan masalah salinan dalam atau salinan dangkal, tidak ada yang Anda lakukan adalah salinan dalam.

Sini:

>>> new = original 

Anda sedang membuat referensi baru ke daftar / dict yang dirujuk oleh aslinya.

di sini:

>>> new = original.copy()
>>> # or
>>> new = list(original) # dict(original)

Anda sedang membuat daftar / dikt baru yang diisi dengan salinan referensi objek yang terkandung dalam wadah asli.


31

Ambil contoh ini:

original = dict(a=1, b=2, c=dict(d=4, e=5))
new = original.copy()

Sekarang mari kita ubah nilai di level 'dangkal' (pertama):

new['a'] = 10
# new = {'a': 10, 'b': 2, 'c': {'d': 4, 'e': 5}}
# original = {'a': 1, 'b': 2, 'c': {'d': 4, 'e': 5}}
# no change in original, since ['a'] is an immutable integer

Sekarang mari kita ubah nilai satu level lebih dalam:

new['c']['d'] = 40
# new = {'a': 10, 'b': 2, 'c': {'d': 40, 'e': 5}}
# original = {'a': 1, 'b': 2, 'c': {'d': 40, 'e': 5}}
# new['c'] points to the same original['d'] mutable dictionary, so it will be changed

8
no change in original, since ['a'] is an immutable integerIni. Ini sebenarnya menjawab pertanyaan yang diajukan.
CivFan

8

Menambahkan ke jawaban kennytm. Ketika Anda melakukan copy parent.copy dangkal () kamus baru dibuat dengan kunci yang sama, tetapi nilai-nilai tidak disalin mereka direferensikan. Jika Anda menambahkan nilai baru ke parent_copy itu tidak akan mempengaruhi orangtua karena parent_copy adalah kamus baru bukan referensi.

parent = {1: [1,2,3]}
parent_copy = parent.copy()
parent_reference = parent

print id(parent),id(parent_copy),id(parent_reference)
#140690938288400 140690938290536 140690938288400

print id(parent[1]),id(parent_copy[1]),id(parent_reference[1])
#140690938137128 140690938137128 140690938137128

parent_copy[1].append(4)
parent_copy[2] = ['new']

print parent, parent_copy, parent_reference
#{1: [1, 2, 3, 4]} {1: [1, 2, 3, 4], 2: ['new']} {1: [1, 2, 3, 4]}

Nilai hash (id) dari parent [1] , parent_copy [1] identik yang menyiratkan [1,2,3] dari induk [1] dan parent_copy [1] disimpan di id 140690938288400.

Tetapi hash dari parent dan parent_copy berbeda yang menyiratkannya. Mereka adalah kamus yang berbeda dan parent_copy adalah kamus baru yang memiliki referensi nilai ke nilai parent


5

"baru" dan "asli" adalah dict yang berbeda, itu sebabnya Anda dapat memperbarui salah satunya saja .. Item tersebut disalin dangkal, bukan dict itu sendiri.


2

Konten disalin dangkal.

Jadi, jika dokumen asli dictberisi yang listlain dictionary, memodifikasi yang asli atau yang dangkal akan memodifikasinya ( listatau yang dict) di yang lain.


1

Di bagian kedua Anda, Anda harus menggunakan new = original.copy()

.copydan =hal-hal yang berbeda.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.