Cara cepat untuk menyalin kamus dengan Python

92

Saya memiliki program Python yang bekerja dengan banyak kamus. Saya harus membuat salinan kamus ribuan kali. Saya memerlukan salinan kunci dan konten terkait. Salinan akan diedit dan tidak boleh ditautkan ke aslinya (mis. Perubahan dalam salinan tidak boleh mempengaruhi aslinya.)

Kunci adalah String, Nilai adalah Integer (0/1).

Saat ini saya menggunakan cara sederhana:

newDict = oldDict.copy()

Memprofil Kode saya menunjukkan bahwa operasi penyalinan memakan sebagian besar waktu.

Apakah ada alternatif yang lebih cepat untuk dict.copy()metode ini? Apa yang tercepat?

Joern
sumber
1
Jika nilainya bisa 0 atau 1, akankah boolmenjadi pilihan yang lebih baik daripada int?
Samir Talwar
5
Dan jika Anda membutuhkan ribuan salinannya, apakah bitmask akan bekerja lebih baik?
Wooble
@Samir tidak ada booldalam Python bernama int.
Santa
Saya setuju, bagaimanapun, bahwa bitmask mungkin lebih efisien untuk Anda (tergantung pada bagaimana Anda menggunakan "dict", sungguh).
Santa
1
Untuk memperjelas, booltipe sebenarnya adalah subclass (subtipe?) Dari inttipe tersebut.
Santa

Jawaban:

64

Melihat sumber C untuk dictoperasi Python , Anda dapat melihat bahwa mereka melakukan penyalinan yang cukup naif (tapi efisien). Ini pada dasarnya bermuara pada panggilan ke PyDict_Merge:

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

Ini melakukan pemeriksaan cepat untuk hal-hal seperti apakah mereka adalah objek yang sama dan jika mereka memiliki objek di dalamnya. Setelah itu, ia melakukan perubahan ukuran / alokasi satu kali ke target dikt dan kemudian menyalin elemen satu per satu. Saya tidak melihat Anda menjadi lebih cepat dari yang ada di dalamnya copy().

Daniel DiPaolo
sumber
1
Sepertinya saya lebih baik menulis ulang kode untuk menghindari penggunaan dict sama sekali - atau menggunakan struktur data yang lebih cepat yang dapat melakukan pekerjaan yang sama. Terima kasih banyak atas jawabannya!
Joern
56

Dict.copy tampaknya lebih cepat, seperti yang Anda katakan.

[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
utdemir
sumber
Terima kasih atas perbandingannya! Akan mencoba untuk menulis ulang kode untuk menghindari penggunaan penyalinan dict di banyak tempat. Terima kasih lagi!
Joern
4
Cara untuk melakukan perbandingan terakhir tanpa menghitung biaya melakukan impor setiap waktu dengan timeit's -sargumen: python -m timeit -s "from copy import copy" "new = copy({1:1, 2:2, 3:3})". Saat Anda melakukannya, tarik kreasi dict juga (untuk semua contoh.)
Thomas Wouters
Mungkin mengulang proses berkali-kali lebih baik karena mungkin ada beberapa fluktuasi dari satu pengambilan gambar tertentu.
xiaohan2012
2
Waktu melakukannya; seperti yang dikatakan itu loop 1000000 kali dan rata-rata.
utdemir
Saya memiliki pengaturan waktu yang bertentangan. a = {b: b untuk b dalam range (10000)} Dalam [5]:% timeit copy (a) 10000 loop, terbaik 3: 186 µs per loop Dalam [6]:% timeit deepcopy (a) 100 loop, terbaik 3: 14.1 ms per loop Dalam [7]:% timeit a.copy () 1000 loop, terbaik 3: 180 µs per loop
Davoud Taghawi-Nejad
12

Dapatkah Anda memberikan contoh kode sehingga saya dapat melihat bagaimana Anda menggunakan copy () dan dalam konteks apa?

Anda bisa menggunakan

new = dict(old)

Tapi saya tidak berpikir itu akan lebih cepat.

MikeVaughan
sumber
5

Saya menyadari ini adalah utas lama, tetapi ini adalah hasil tinggi di mesin pencari untuk "dikt copy python", dan hasil teratas untuk "kinerja salinan dikt", dan saya yakin ini relevan.

Dari Python 3.7, newDict = oldDict.copy()lebih cepat hingga 5,5x dari sebelumnya. Khususnya, saat ini, newDict = dict(oldDict)tampaknya kinerja tersebut tidak mengalami peningkatan.

Ada sedikit lebih banyak informasi di sini .

iandioch
sumber
3

Bergantung pada hal-hal yang Anda tinggalkan untuk spekulasi, Anda mungkin ingin membungkus kamus asli dan melakukan semacam copy-on-write.

"Salin" kemudian adalah kamus yang mencari barang di kamus "induk", jika belum berisi kunci --- tetapi barang modifikasi itu sendiri.

Ini mengasumsikan bahwa Anda tidak akan mengubah yang asli dan pencarian tambahan tidak berakhir dengan biaya lebih.

Alex Brasetvik
sumber
2

Pengukurannya tergantung pada ukuran kamus. Untuk 10.000 entri, salinan (d) dan d.copy () hampir sama.

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
Davoud Taghawi-Nejad
sumber