Cách nhanh chóng để sao chép từ điển bằng Python


92

Tôi có một chương trình Python hoạt động với từ điển rất nhiều. Tôi phải sao chép từ điển hàng nghìn lần. Tôi cần một bản sao của cả khóa và nội dung liên quan. Bản sao sẽ được chỉnh sửa và không được liên kết với bản gốc (ví dụ: những thay đổi trong bản sao không được ảnh hưởng đến bản gốc.)

Khóa là Chuỗi, Giá trị là Số nguyên (0/1).

Tôi hiện đang sử dụng một cách đơn giản:

newDict = oldDict.copy()

Việc lập hồ sơ Mã của tôi cho thấy rằng thao tác sao chép chiếm hầu hết thời gian.

Có lựa chọn thay thế nhanh hơn cho dict.copy()phương pháp này không? Điều gì sẽ là nhanh nhất?


1
Nếu giá trị có thể là 0 hoặc 1, thì boollựa chọn nào tốt hơn là một int?
Samir Talwar

5
Và nếu bạn cần hàng nghìn bản sao của chúng, mặt nạ bit có hoạt động tốt hơn không?
Wooble

@Samir không boolcó tên trong Python int.
Ông già Noel

Tuy nhiên, tôi đồng ý rằng bitmask có thể hiệu quả hơn đối với bạn (thực sự tùy thuộc vào cách bạn sử dụng "dict" này).
Ông già Noel

1
Để làm rõ, boolkiểu thực sự là một lớp con (kiểu con?) Của intkiểu.
Ông già Noel

Câu trả lời:


64

Nhìn vào nguồn C cho các dicthoạt động Python , bạn có thể thấy rằng chúng thực hiện một bản sao khá ngây thơ (nhưng hiệu quả). Về cơ bản, nó tóm gọn lại một cuộc gọi tới PyDict_Merge:

PyDict_Merge(PyObject *a, PyObject *b, int override)

Điều này thực hiện kiểm tra nhanh những thứ như liệu chúng có phải là cùng một đối tượng hay không và nếu chúng có các đối tượng trong đó. Sau đó, nó thực hiện thay đổi kích thước / phân bổ một lần cho lệnh đích và sau đó sao chép từng phần tử một. Tôi không thấy bạn nhanh hơn nhiều so với cài sẵn copy().


1
Có vẻ như tốt hơn là tôi nên viết lại mã để tránh sử dụng các dấu chấm - hoặc sử dụng cấu trúc dữ liệu nhanh hơn có thể thực hiện công việc tương tự. Cảm ơn bạn rất nhiều cho câu trả lời!
Joern

56

Rõ ràng là dict.copy nhanh hơn, như bạn nói.

[utdmr@utdmr-arch ~]$ python -m timeit -s "d={1:1, 2:2, 3:3}" "new = d.copy()"
1000000 loops, best of 3: 0.238 usec per loop
[utdmr@utdmr-arch ~]$ python -m timeit -s "d={1:1, 2:2, 3:3}" "new = dict(d)"
1000000 loops, best of 3: 0.621 usec per loop
[utdmr@utdmr-arch ~]$ python -m timeit -s "from copy import copy; d={1:1, 2:2, 3:3}" "new = copy(d)"
1000000 loops, best of 3: 1.58 usec per loop

Cảm ơn vì sự so sánh! Sẽ cố gắng viết lại mã để tránh việc sử dụng sao chép chính tả ở hầu hết các nơi. Cảm ơn một lần nữa!
Joern

4
Cách làm việc so sánh cuối cùng mà không đếm chi phí kinh nhập khẩu mỗi lần là với timeit's -sluận: python -m timeit -s "from copy import copy" "new = copy({1:1, 2:2, 3:3})". Trong khi bạn đang ở đó, hãy kéo sáng tạo chính tả ra (đối với tất cả các ví dụ.)
Thomas Wouters

Có thể lặp lại quy trình nhiều lần sẽ tốt hơn vì có thể có một số biến động của một cảnh quay cụ thể.
xiaohan2012

2
Timeit thực hiện điều đó; như nó nói nó lặp lại 1000000 lần và tính trung bình.
utdemir

Tôi có thời gian mâu thuẫn. a = {b: b for b in range (10000)} Trong [5]:% timeit copy (a) 10000 vòng, tốt nhất là 3: 186 µs mỗi vòng Trong [6]:% timeit deepcopy (a) 100 vòng, tốt nhất của 3: 14.1 ms mỗi vòng lặp trong [7]:% timeit a.copy () 1000 vòng, tốt nhất là 3: 180 ms mỗi vòng lặp
Davoud Taghawi-Nejad

12

Bạn có thể cung cấp mẫu mã để tôi có thể biết bạn đang sử dụng copy () như thế nào và trong ngữ cảnh nào không?

Bạn đã có thể sử dụng

new = dict(old)

Nhưng tôi không nghĩ rằng nó sẽ nhanh hơn.


5

Tôi nhận thấy đây là một chủ đề cũ, nhưng đây là một kết quả cao trong các công cụ tìm kiếm cho "dict copy python" và kết quả hàng đầu cho "hiệu suất sao chép dict" và tôi tin rằng điều này có liên quan.

Từ Python 3.7, newDict = oldDict.copy()nhanh hơn tới 5,5 lần so với trước đây. Đáng chú ý, ngay bây giờ, newDict = dict(oldDict)dường như không có sự gia tăng hiệu suất này.

Có một chút thông tin ở đây .


3

Tùy thuộc vào những thứ bạn để suy đoán, bạn có thể muốn bọc từ điển gốc và thực hiện loại sao chép-ghi-chép.

"Bản sao" sau đó là một từ điển tra cứu nội dung trong từ điển "mẹ", nếu nó chưa chứa khóa --- nhưng tự nó nhồi nhét các sửa đổi.

Điều này giả định rằng bạn sẽ không sửa đổi bản gốc và các lần tra cứu bổ sung không làm tốn thêm chi phí.


2

Tuy nhiên, các phép đo phụ thuộc vào kích thước từ điển. Đối với 10000 mục, copy (d) và d.copy () gần như giống nhau.

a = {b: b for b in range(10000)} 
In [5]: %timeit copy(a)
10000 loops, best of 3: 186 µs per loop
In [6]: %timeit deepcopy(a)
100 loops, best of 3: 14.1 ms per loop
In [7]: %timeit a.copy()
1000 loops, best of 3: 180 µs per loop
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.