สำเนา dict แบบละเอียดในไพ ธ อน


341

ฉันต้องการที่จะทำสำเนาลึกของdictในหลาม แต่น่าเสียดายวิธีการไม่ได้อยู่สำหรับ.deepcopy() dictฉันจะทำอย่างไร

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

3บรรทัดสุดท้ายที่ควรจะเป็น

ฉันต้องการที่ปรับเปลี่ยนในไม่ส่งผลกระทบต่อภาพรวมmy_dictmy_copy

ฉันจะทำอย่างไร โซลูชันควรใช้งานได้กับ Python 3.x


3
ฉันไม่รู้ว่ามันซ้ำกันหรือไม่ แต่นี่คือstackoverflow.com/questions/838642/python-dictionary-deepcopyใกล้สุด ๆ
charleslparker

คำตอบ:


473

เกี่ยวกับ:

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

Python 2 หรือ 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
แน่นอนว่าเหมาะกับตัวอย่างที่เกินจริงที่ฉันให้ไว้ กุญแจของฉันไม่ใช่ตัวเลข แต่เป็นวัตถุ ถ้าฉันอ่านเอกสารโมดูลการคัดลอกฉันต้องประกาศ __copy __ () / __ deepcopy __ () วิธีการสำหรับคีย์ ขอบคุณมากที่พาฉันไปที่นั่น!
Olivier Grégoire

3
มีความแตกต่างในรหัส Python 3.2 และ 2.7 หรือไม่ พวกเขาดูเหมือนฉัน ถ้าเป็นเช่นนั้นจะเป็นการดีกว่าบล็อกหนึ่งรหัสและคำสั่ง "ใช้งานได้กับทั้ง Python 3 และ 2"
MestreLion

30
นอกจากนี้ยังมีมูลค่าการพูดถึงcopy.deepcopyไม่ปลอดภัยด้าย เรียนรู้วิธีนี้ยาก บนมืออื่น ๆ ขึ้นอยู่กับกรณีการใช้งานของคุณjson.loads(json.dumps(d)) คือความปลอดภัยด้ายและทำงานได้ดี
ปล้น

1
@rob คุณควรโพสต์ความคิดเห็นนั้นเป็นคำตอบ มันเป็นทางเลือกที่ทำงานได้ ความแตกต่างนิดหน่อยความปลอดภัยของด้ายเป็นความแตกต่างที่สำคัญ
BuvinJ

3
@BuvinJ ปัญหาคือjson.loadsไม่ได้แก้ปัญหาสำหรับทุกกรณีการใช้งานที่dictคุณลักษณะของหลามไม่ได้ JSON อนุกรม มันอาจช่วยให้ผู้ที่จัดการกับโครงสร้างข้อมูลอย่างง่ายจาก API เช่น แต่ฉันคิดว่ามันไม่เพียงพอที่จะตอบคำถามของ OP ได้อย่างเต็มที่
ปล้น

36

dict.copy ()เป็นฟังก์ชั่นการคัดลอกที่ตื้นสำหรับ
id ของพจนานุกรม เป็นฟังก์ชันในตัวที่ให้ที่อยู่ของตัวแปร

ก่อนอื่นคุณต้องเข้าใจ "ทำไมปัญหานี้จึงเกิดขึ้นโดยเฉพาะ"

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

ที่อยู่ของรายการมีอยู่ในทั้ง dicts สำหรับคีย์ 'a' กำลังชี้ไปยังตำแหน่งเดียวกัน
ดังนั้นเมื่อคุณเปลี่ยนค่าของรายการใน my_dict รายการใน my_copy ก็จะเปลี่ยนเช่นกัน


โซลูชันสำหรับโครงสร้างข้อมูลที่กล่าวถึงในคำถาม:

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

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

หรือคุณสามารถใช้ Deepcopy ได้ตามที่กล่าวไว้ข้างต้น


4
โซลูชันของคุณใช้ไม่ได้กับพจนานุกรมที่ซ้อนกัน Deepcopy ดีกว่าด้วยเหตุผลนั้น
Charles Plager

2
@CharlesPlager เห็นด้วย! แต่คุณควรสังเกตว่าการแบ่งส่วนรายการไม่สามารถใช้กับ dict value[:]ได้ การแก้ปัญหาสำหรับโครงสร้างข้อมูลเฉพาะที่กล่าวถึงในคำถามแทนที่จะเป็นโซลูชันสากล
theBuzzyCoder

17

Python 3.x

จากการคัดลอกการนำเข้า deepcopy

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

หากไม่มี Deepcopy ฉันไม่สามารถลบพจนานุกรมชื่อโฮสต์ออกจากภายในพจนานุกรมโดเมนของฉัน

หากไม่มี deepcopy ฉันจะได้รับข้อผิดพลาดต่อไปนี้:

"RuntimeError: dictionary changed size during iteration"

... เมื่อฉันพยายามที่จะลบองค์ประกอบที่ต้องการจากพจนานุกรมของฉันภายในพจนานุกรมอื่น

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

โดเมนเป็นวัตถุพจนานุกรม

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

ตัวอย่างผลลัพธ์: [orginal] domains = {'localdomain': {'localhost': {'all': '4000'}}}

[ใหม่] โดเมน = {'localdomain': {}}}

ดังนั้นสิ่งที่เกิดขึ้นที่นี่คือฉันทำซ้ำสำเนาของพจนานุกรมแทนที่จะวนซ้ำพจนานุกรมเอง ด้วยวิธีนี้คุณสามารถลบองค์ประกอบได้ตามต้องการ


-3

ฉันชอบและเรียนรู้มากมายจาก Lasse V. Karlsen ฉันแก้ไขมันเป็นตัวอย่างต่อไปนี้ซึ่งเน้นความแตกต่างที่ดีระหว่างสำเนาพจนานุกรมตื้นและสำเนาลึก:

    import copy

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

ตอนนี้ถ้าคุณเปลี่ยน

    my_dict['a'][2] = 7

และทำ

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

คุณได้รับ

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

1
ทำไมคุณคิดว่าคำตอบนี้จะแตกต่างจากคำตอบ Lasse V. Karlsen ของ ? มันเพิ่มอะไรที่คำตอบอื่นไม่พูด
Olivier Grégoire

สวัสดีโอลิเวียร์! ฉันไม่ได้พยายามที่จะใช้ประโยชน์จากคำตอบของLasse V. Karlsen - เขาแก้ปัญหาที่ฉันมีและฉันเป็นหนี้เขา ความคิดเห็นของฉันไม่แตกต่างกันมันเป็นแค่เสริม ด้วยเหตุผลง่ายๆว่ามันตรงกันข้าม "คัดลอก" กับ "Deepcopy" นี่คือสาเหตุของปัญหาของฉันเพราะฉันเข้าใจผิดเมื่อใช้พวกเขาในแบบที่เทียบเท่า ไชโย
Rafael Monteiro

-9

ทางออกที่ง่ายกว่า (ในมุมมองของฉัน) คือการสร้างพจนานุกรมใหม่และอัปเดตด้วยเนื้อหาของพจนานุกรมเก่า:

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

ปัญหาเกี่ยวกับวิธีการนี้คืออาจไม่ลึกพอ นั่นคือไม่ลึกซ้ำ ดีพอสำหรับวัตถุธรรมดา แต่ไม่เหมาะสำหรับพจนานุกรมที่ซ้อนกัน นี่คือตัวอย่างที่อาจไม่ลึกพอ:

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 () ฉันสามารถกำจัดพฤติกรรมกึ่งตื้น แต่ฉันคิดว่าต้องตัดสินใจว่าแนวทางใดที่เหมาะกับการสมัครของคุณ ในกรณีส่วนใหญ่คุณอาจไม่สนใจ แต่ควรตระหนักถึงข้อผิดพลาดที่อาจเกิดขึ้น ... ตัวอย่างสุดท้าย:

import copy

my_copy2 = copy.deepcopy( my_dict3 )

my_dict1['b']='99'

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

12
สิ่งนี้ทำให้สำเนาของ dict ตื้น ๆ ซึ่งไม่ใช่สิ่งที่ผู้ถามถาม วัตถุที่บรรจุอยู่นั้นจะไม่ถูกคัดลอกมาเอง และวิธีการทำสำเนาแบบตื้นที่ง่ายกว่าก็คือmy_dict.copy()!
Blckknght
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.