Bản sao sâu của một dict trong python


340

Tôi muốn tạo một bản sao sâu của một dictcon trăn. Thật không may, .deepcopy()phương pháp không tồn tại cho dict. Làm thế nào để làm điều đó?

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

Dòng cuối cùng nên được 3.

Tôi muốn sửa đổi my_dictđó không ảnh hưởng đến ảnh chụp nhanh my_copy.

Làm thế nào để làm điều đó? Giải pháp phải tương thích với Python 3.x.


3
Tôi không biết nếu đó là một bản sao, nhưng điều này: stackoverflow.com/questions/838642/python-dipedia-deepcopy rất gần.
charleslparker

Câu trả lời:


472

Làm thế nào về:

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

Python 2 hoặc 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
Thật vậy, nó làm việc cho ví dụ đơn giản hóa tôi đã đưa ra. Chìa khóa của tôi không phải là số mà là đồ vật. Nếu tôi đọc tài liệu mô-đun sao chép, tôi phải khai báo phương thức __copy __ () / __ deepcopy __ () cho các khóa. Cảm ơn bạn rất nhiều vì đã dẫn tôi đến đó!
Olivier Grégoire

3
Có sự khác biệt nào trong mã Python 3.2 và 2.7 không? Họ có vẻ giống hệt tôi. Nếu vậy, sẽ tốt hơn một khối mã và một câu lệnh "Hoạt động cho cả Python 3 và 2"
MestreLion

30
Nó cũng đáng được đề cập copy.deepcopykhông phải là chủ đề an toàn. Đã học được điều này một cách khó khăn. Mặt khác, tùy thuộc vào trường hợp sử dụng của bạn, json.loads(json.dumps(d)) luồng an toàn và hoạt động tốt.
cướp

1
@rob bạn nên đăng bình luận đó như một câu trả lời. Nó là một sự thay thế khả thi. Các sắc thái an toàn chủ đề là một sự phân biệt quan trọng.
BuvinJ

3
@BuvinJ Vấn đề là json.loadskhông giải quyết được vấn đề cho tất cả các trường hợp sử dụng trong đó dictcác thuộc tính python không được tuần tự hóa JSON. Nó có thể giúp những người chỉ xử lý các cấu trúc dữ liệu đơn giản, ví dụ từ API, nhưng tôi không nghĩ rằng đó là một giải pháp đủ để trả lời đầy đủ câu hỏi của OP.
cướp

35

dict.copy () là một hàm sao chép nông cho
id từ điển là hàm dựng sẵn cung cấp cho bạn địa chỉ của biến

Đầu tiên bạn cần hiểu "tại sao vấn đề đặc biệt này lại xảy ra?"

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

Địa chỉ của danh sách có trong cả hai ký tự cho khóa 'a' đang trỏ đến cùng một vị trí.
Do đó, khi bạn thay đổi giá trị của danh sách trong my_dict, danh sách trong my_copy cũng thay đổi.


Giải pháp cho cấu trúc dữ liệu được đề cập trong câu hỏi:

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

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

Hoặc bạn có thể sử dụng deepcopy như đã đề cập ở trên.


4
Giải pháp của bạn không hoạt động cho các từ điển lồng nhau. Deepcopy là thích hợp hơn cho lý do đó.
Charles Plager

2
@CharlesPlager Đồng ý! Nhưng bạn cũng nên chú ý rằng việc cắt danh sách không hoạt động trên dict value[:]. Giải pháp là cho cấu trúc dữ liệu cụ thể được đề cập trong câu hỏi chứ không phải là một giải pháp phổ quát.
theBuzzyCoder

16

Python 3.x

từ bản sao nhập sâu

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

Nếu không có deepcopy, tôi không thể xóa từ điển tên máy chủ trong từ điển tên miền của mình.

Nếu không có deepcopy tôi nhận được lỗi sau:

"RuntimeError: dictionary changed size during iteration"

... khi tôi cố gắng loại bỏ yếu tố mong muốn khỏi từ điển của mình bên trong một từ điển khác.

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

tên miền là một đối tượng từ điển

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

Kết quả ví dụ: [orginal] domain = {'localdomain': {'localhost': {'all': '4000'}}}

[mới] tên miền = {' tên miền cục bộ': {}}}

Vì vậy, những gì đang diễn ra ở đây là tôi đang lặp đi lặp lại một bản sao của một từ điển chứ không phải lặp đi lặp lại trên chính từ điển. Với phương pháp này, bạn có thể loại bỏ các yếu tố khi cần thiết.


-2

Tôi thích và học được rất nhiều từ Lasse V. Karlsen. Tôi đã sửa đổi nó thành ví dụ sau, trong đó nêu rõ khá rõ sự khác biệt giữa các bản sao từ điển nông và bản sao sâu:

    import copy

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

Bây giờ nếu bạn thay đổi

    my_dict['a'][2] = 7

và làm

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

bạn lấy

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

Tại sao bạn nghĩ câu trả lời này khác với câu trả lời của Lasse V. Karlsen ? Nó thêm gì mà câu trả lời khác không nói?
Olivier Grégoire

Xin chào, Olivier! Tôi không cố gắng để nhận công đức cho câu trả lời của Lasse V. Karlsen - về cơ bản anh ấy đã giải quyết được vấn đề mà tôi gặp phải, và tôi mắc nợ anh ấy. Nhận xét của tôi không khác, nó chỉ là bổ sung. Vì lý do đơn giản là nó tương phản "sao chép" với "deepcopy". Đây là nguồn gốc của vấn đề của tôi, vì tôi đã nhầm khi sử dụng chúng theo cách tương đương. Chúc mừng.
Rafael Monteiro

-8

Một giải pháp đơn giản hơn (theo quan điểm của tôi) là tạo một từ điển mới và cập nhật nó với nội dung của từ cũ:

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

Vấn đề với phương pháp này là nó có thể không 'đủ sâu'. tức là không đệ quy sâu. đủ tốt cho các đối tượng đơn giản nhưng không cho từ điển lồng nhau. Đây là một ví dụ mà nó có thể không đủ sâu:

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

Bằng cách sử dụng Deepcopy () tôi có thể loại bỏ hành vi bán nông, nhưng tôi nghĩ người ta phải quyết định cách tiếp cận nào phù hợp với ứng dụng của bạn. Trong hầu hết các trường hợp, bạn có thể không quan tâm, nhưng nên biết về những cạm bẫy có thể xảy ra ... ví dụ cuối cùng:

import copy

my_copy2 = copy.deepcopy( my_dict3 )

my_dict1['b']='99'

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

12
Điều này làm cho một bản sao nông của dict, đó không phải là những gì người hỏi đã yêu cầu. Các đối tượng mà nó chứa không được sao chép. Và một cách dễ dàng hơn để sao chép nông là my_dict.copy()!
Blckknght
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.