Dict.copy () - sığ mı derin mi?


429

İçin belgeleri okurken dict.copy(), sözlüğün sığ bir kopyasını yaptığını söylüyor. Aynı şey takip ettiğim kitap için de geçerli (Beazley'in Python Referansı):

M.copy () yöntemi, bir eşleme nesnesinde bulunan öğelerin sığ bir kopyasını oluşturur ve bunları yeni bir eşleme nesnesine yerleştirir.

Bunu düşün:

>>> 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}

Bu yüzden originalsığ bir kopya yaptığımdan beri bunun değerini güncelleyeceğini varsaydım (ve 'c': 3'ü ekleyeceğim). Bir liste için yaparsanız:

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

Bu beklendiği gibi çalışır.

Her ikisi de sığ kopyalar olduğundan, neden dict.copy()beklediğim gibi çalışmıyor? Ya da sığ ve derin kopyalama anlayışım kusurlu mu?


2
"Sığ" açıklamak değil tuhaf. İçeriden bilgi, göz kırp. Sadece dict ve anahtarlar bir kopyadır, ancak ilk seviyenin içindeki iç içe dicimler referanstır, örneğin bir döngüde silinemez. Dolayısıyla Python'un dict.copy () yöntemi bu durumda ne kullanışlı ne de sezgiseldir. Sorunuz için teşekkürler.
gseattle

Yanıtlar:


990

"Sığ kopyalama" ile, sözlük içeriğinin değere göre kopyalanmadığı, sadece yeni bir referans oluşturduğu anlamına gelir .

>>> 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]})

Bunun aksine, derin bir kopya tüm içeriği değere göre kopyalar.

>>> 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]})

Yani:

  1. b = a: Referans atama, Marka ave baynı nesneyi işaret eder.

    'A = b': 'a' ve 'b' örneklerinin her ikisi de '{1: L}', 'L' '[1, 2, 3]' yi gösterir.

  2. b = a.copy(): Sığ kopyalama ave biki yalıtılmış nesne haline gelecek, ancak içerikleri hala aynı referansı paylaşıyor

    'B = a.copy ()': 'a', '{1: L}' işaretini gösterir, 'b' '{1: M}', 'L' ve 'M' işaretini her ikisi de '[ 1, 2, 3] '.

  3. b = copy.deepcopy(a): Derin kopyalama ave b'yapısı ve içeriği haline tamamen izole edilmiştir.

    'B = copy.deepcopy (a)': 'a', '{1: L}', 'L', '[1, 2, 3]' yi gösterir;  'b', '{1: M}', 'M' ise farklı bir '[1, 2, 3]' örneğini gösterir.


Güzel cevap, ama ilk cümlede dilbilgisi hatasını düzeltmeyi düşünebilirsiniz. Ve Ltekrar kullanmamak için hiçbir neden yok b. Bunu yapmak örneği basitleştirecektir.
Tom Russell

@kennytm: Aslında ilk iki örnek arasındaki fark nedir? Aynı sonucu elde edersiniz, ancak biraz farklı iç uygulama, ancak bunun için ne önemli?
JavaSa

@TomRussell: Ya da herhangi biri, bu soru oldukça eski olduğundan, açıklama sorum herkes için
JavaSa

@JavaSa Diyelim ki yaparsanız önemlidir b[1][0] = 5. Eğer bsığ kopyasıdır, sadece değiştirdik a[1][0].
Tom Russell

2
Büyük açıklama, ... gerçekten günümü kurtardı! Teşekkürler ... Bu aynı pphon listesi, str ve diğer veri türleri için uygulanabilir mi?
Bhuro

38

Bu derin kopya veya sığ kopya meselesi değil, yaptığınız hiçbir şey derin kopya değil.

Buraya:

>>> new = original 

orijinal tarafından başvurulan listeye / dict'e yeni bir referans oluşturuyorsunuz.

buradayken:

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

orijinal kapsayıcıda bulunan nesnelerin referanslarının bir kopyasıyla dolu yeni bir liste / dikte oluşturuyorsunuz.


31

Bu örneği ele alalım:

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

Şimdi 'sığ' (ilk) seviyedeki bir değeri değiştirelim:

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

Şimdi değeri bir seviye daha derine değiştirelim:

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 integerBu. Aslında sorulan soruya cevap veriyor.
CivFan

8

Kennytm'in cevabına ekleme. Eğer sığ kopya yaptığınızda parent.copy () Yeni bir sözlük Aynı anahtarlara sahip oluşturulur, ancak değerler kopyalanmaz onlar referenced.If sen yeni bir değer katmak parent_copy onu olmaz etkisi ebeveyn çünkü parent_copy yeni sözlüğü referans değil.

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]}

Ebeveyn [1] , parent_copy [1] ' in karma (id) değeri, 140690938288400 kimliğinde depolanan ebeveyn [1] ve parent_copy [1]' in [1,2,3] değerini ima eden aynıdır .

Ancak parent ve parent_copy karması farklıdır, bu da farklı sözlüklerdir ve parent_copy , ebeveynin değerlerine referans değerleri olan yeni bir sözlüktür.


5

"Yeni" ve "orijinal" farklı diktelerdir, bu yüzden bunlardan sadece birini güncelleyebilirsiniz .. Öğeler diktenin kendisi değil, sığ kopyalanmıştır.


2

İçindekiler sığ kopyalanmıştır.

Dolayısıyla, orijinal dictbir listveya başka bir içeriyorsa dictionary, orijinalde veya sığ kopyasında değiştirmek , diğerinde ( listveya veya dict) değiştirecektir .


1

İkinci bölümde, new = original.copy()

.copyve =farklı şeylerdir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.