Python'da bir diktenin derin kopyası


341

Python'da bir derin kopyasını yapmak istiyorum dict. Ne yazık ki .deepcopy()yöntem için mevcut değil dict. Bunu nasıl yaparım?

>>> my_dict = {'a': [1, 2, 3], 'b': [4, 5, 6]}
>>> my_copy = my_dict.deepcopy()
Traceback (most recent calll last):
  File "<stdin>", line 1, in <module>
AttributeError: 'dict' object has no attribute 'deepcopy'
>>> my_copy = my_dict.copy()
>>> my_dict['a'][2] = 7
>>> my_copy['a'][2]
7

Son satır olmalıdır 3.

Bu değişikliklerin my_dictanlık görüntüyü etkilememesini istiyorum my_copy.

Bunu nasıl yaparım? Çözüm Python 3.x ile uyumlu olmalıdır.


3
Bunun bir kopya olup olmadığını bilmiyorum, ama bu: stackoverflow.com/questions/838642/python-dictionary-deepcopy çok yakın.
charleslparker

Yanıtlar:


473

Nasıl olur:

import copy
d = { ... }
d2 = copy.deepcopy(d)

Python 2 veya 3:

Python 3.2 (r32:88445, Feb 20 2011, 21:30:00) [MSC v.1500 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import copy
>>> my_dict = {'a': [1, 2, 3], 'b': [4, 5, 6]}
>>> my_copy = copy.deepcopy(my_dict)
>>> my_dict['a'][2] = 7
>>> my_copy['a'][2]
3
>>>

16
Gerçekten bu verdiğim basitleştirilmiş örnek için çalışıyor. Anahtarlarım sayılar değil nesneler. Kopyalama modülü belgelerini okursam, anahtarlar için bir __copy __ () / __ deepcopy __ () yöntemi bildirmem gerekir. Beni oraya götürdüğün için çok teşekkürler!
Olivier Grégoire

3
Python 3.2 ve 2.7 kodlarında herhangi bir fark var mı? Bana benziyorlar. Öyleyse, tek bir kod bloğu ve "Hem Python 3 hem de 2 için çalışır" ifadesi daha iyi olurdu
MestreLion

30
Ayrıca bahsetmeye değer copy.deepcopyiplik güvenli değil. Bunu zor yoldan öğrendim. Öte yandan, kullanım durumuna bağlı olarak json.loads(json.dumps(d)) , iplik güvenli ve iyi çalışıyor.
soymak

1
@rob bu yorumu bir cevap olarak göndermelisiniz. Geçerli bir alternatiftir. İplik güvenliği nüansı önemli bir ayrımdır.
BuvinJ

3
@BuvinJ Sorun, json.loadspython dictözniteliklerinin JSON serileştirilemediği tüm kullanım durumları için sorunu çözmemesi . Örneğin, bir API'den sadece basit veri yapılarıyla uğraşanlara yardımcı olabilir, ancak OP'nin sorusuna tam olarak cevap vermenin bir çözüm olduğunu düşünmüyorum.
soymak

36

dict.copy () sözlük
kimliği için sığ bir kopyalama işlevi değişkenin adresini veren yerleşik bir işlevdir

Öncelikle "bu özel sorun neden oluyor?"

In [1]: my_dict = {'a': [1, 2, 3], 'b': [4, 5, 6]}

In [2]: my_copy = my_dict.copy()

In [3]: id(my_dict)
Out[3]: 140190444167808

In [4]: id(my_copy)
Out[4]: 140190444170328

In [5]: id(my_copy['a'])
Out[5]: 140190444024104

In [6]: id(my_dict['a'])
Out[6]: 140190444024104

Her iki 'a' tuşu için dikte bulunan listenin adresi aynı yeri gösteriyor.
Bu nedenle, my_dict içindeki listenin değerini değiştirdiğinizde, my_copy içindeki liste de değişir.


Soruda bahsedilen veri yapısı için çözüm:

In [7]: my_copy = {key: value[:] for key, value in my_dict.items()}

In [8]: id(my_copy['a'])
Out[8]: 140190444024176

Veya yukarıda belirtildiği gibi deepcopy kullanabilirsiniz.


4
Çözümünüz iç içe sözlükler için işe yaramıyor. bu nedenle deepcopy tercih edilir.
Charles Plager

2
@CharlesPlager Kabul Edildi! Ancak, liste dilimlemenin dikte üzerinde çalışmadığını da fark etmelisiniz value[:]. Çözüm, evrensel bir çözümden ziyade soruda belirtilen özel veri yapısı içindi.
theBuzzyCoder

17

Python 3.x

kopya içe aktarma derin kopyasından

my_dict = {'one': 1, 'two': 2}
new_dict_deepcopy = deepcopy(my_dict)

Deepcopy olmadan, ana bilgisayar adı sözlüğünü etki alanı sözlüğümden kaldıramıyorum.

Deepcopy olmadan aşağıdaki hatayı alıyorum:

"RuntimeError: dictionary changed size during iteration"

... istediğim öğeyi sözlüğümden başka bir sözlüğün içinde kaldırmaya çalıştığımda

import socket
import xml.etree.ElementTree as ET
from copy import deepcopy

domain bir sözlük nesnesidir

def remove_hostname(domain, hostname):
    domain_copy = deepcopy(domain)
    for domains, hosts in domain_copy.items():
        for host, port in hosts.items():
           if host == hostname:
                del domain[domains][host]
    return domain

Örnek çıktı: [orjinal] alanlar = {'localdomain': {'localhost': {'all': '4000'}}}

[yeni] alanlar = {'localdomain': {}}}

Burada olan şu ki, sözlüğün kendisini yinelemek yerine sözlüğün bir kopyası üzerinde yineleme yapıyorum. Bu yöntemle, öğeleri gerektiği gibi kaldırabilirsiniz.


-3

Lasse V. Karlsen'den çok hoşlandım ve öğrendim. Bunu, sığ sözlük kopyaları ve derin kopyalar arasındaki farkı oldukça iyi vurgulayan aşağıdaki örneğe değiştirdim:

    import copy

    my_dict = {'a': [1, 2, 3], 'b': [4, 5, 6]}
    my_copy = copy.copy(my_dict)
    my_deepcopy = copy.deepcopy(my_dict)

Şimdi değişirsen

    my_dict['a'][2] = 7

ve yap

    print("my_copy a[2]: ",my_copy['a'][2],",whereas my_deepcopy a[2]: ", my_deepcopy['a'][2])

anladın

    >> my_copy a[2]:  7 ,whereas my_deepcopy a[2]:  3

1
Neden bu cevabın Lasse V. Karlsen'in cevabından farklı olduğunu düşünüyorsunuz ? Diğer cevabın söylemediği ne ekliyor?
Olivier Grégoire

Selam Olivier! Lasse V. Karlsen'in cevabının değerini almaya çalışmıyorum - temelde yaşadığım problemi çözdü ve ona borçluyum. Benim yorumum farklı değil, sadece tamamlayıcı. Basit bir nedenden ötürü "kopya" yı "derin kopya" ile karşılaştırır. Sorunumun kaynağı buydu, çünkü onları eşdeğer bir şekilde kullanırken yanılmışım. Şerefe.
Rafael Monteiro

-9

Daha basit (benim görüşüme göre) bir çözüm, yeni bir sözlük oluşturmak ve eskisinin içeriğiyle güncellemek:

my_dict={'a':1}

my_copy = {}

my_copy.update( my_dict )

my_dict['a']=2

my_dict['a']
Out[34]: 2

my_copy['a']
Out[35]: 1

Bu yaklaşımdaki sorun 'yeterince derin' olmayabilir. yani özyinelemeli olarak derin değildir. basit nesneler için yeterince iyi ama iç içe sözlükler için değil. İşte yeterince derin olmayabilir bir örnek:

my_dict1={'b':2}

my_dict2={'c':3}

my_dict3={ 'b': my_dict1, 'c':my_dict2 }

my_copy = {}

my_copy.update( my_dict3 )

my_dict1['b']='z'

my_copy
Out[42]: {'b': {'b': 'z'}, 'c': {'c': 3}}

Deepcopy () kullanarak yarı sığ davranışı ortadan kaldırabilirim, ancak bence başvurunuz için hangi yaklaşımın doğru olduğuna karar vermelisiniz. Çoğu durumda umursamayabilirsiniz, ancak olası tuzakların farkında olmalısınız ... son örnek:

import copy

my_copy2 = copy.deepcopy( my_dict3 )

my_dict1['b']='99'

my_copy2
Out[46]: {'b': {'b': 'z'}, 'c': {'c': 3}}

12
Bu, sorgulayıcının istediği şey olmayan diktinin sığ bir kopyasını yapar. İçerdiği nesneler kendileri kopyalanmaz. Ve sığ kopyalamanın daha kolay bir yolu my_dict.copy()!
Blckknght
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.