วิธีที่รวดเร็วในการคัดลอกพจนานุกรมใน Python


92

ฉันมีโปรแกรม Python ที่ทำงานร่วมกับพจนานุกรมได้มากมาย ฉันต้องทำสำเนาพจนานุกรมหลายพันครั้ง ฉันต้องการสำเนาของทั้งคีย์และเนื้อหาที่เกี่ยวข้อง สำเนาจะถูกแก้ไขและจะต้องไม่เชื่อมโยงกับต้นฉบับ (เช่นการเปลี่ยนแปลงในสำเนาจะต้องไม่ส่งผลกระทบต่อต้นฉบับ)

คีย์คือสตริงค่าคือจำนวนเต็ม (0/1)

ปัจจุบันฉันใช้วิธีง่ายๆ:

newDict = oldDict.copy()

การสร้างโปรไฟล์รหัสของฉันแสดงให้เห็นว่าการดำเนินการคัดลอกใช้เวลาส่วนใหญ่

มีทางเลือกอื่นที่เร็วกว่าสำหรับdict.copy()วิธีนี้หรือไม่? อะไรจะเร็วที่สุด?


1
ถ้าค่าสามารถเป็น 0 หรือ 1 boolได้จะเป็นทางเลือกที่ดีกว่า a int?
Samir Talwar

5
และหากคุณต้องการสำเนาหลายพันชุด bitmasks จะทำงานได้ดีกว่านี้หรือไม่?
Wooble

@Samir ไม่ได้boolอยู่ในชื่อ Python intอยู่ดี
ซานต้า

แม้ว่าฉันยอมรับว่า bitmask อาจมีประสิทธิภาพมากกว่าสำหรับคุณ (ขึ้นอยู่กับว่าคุณใช้ "dict" นี้อย่างไร)
ซานต้า

1
เพื่อชี้แจงboolประเภทนี้เป็นคลาสย่อย (ประเภทย่อย?) ของintประเภท
ซานต้า

คำตอบ:


64

เมื่อดูที่ซอร์ส CสำหรับการdictดำเนินการPython คุณจะเห็นว่าพวกเขาทำสำเนาที่ไร้เดียงสา (แต่มีประสิทธิภาพ) โดยพื้นฐานแล้วจะทำให้เกิดการเรียกร้องให้PyDict_Merge:

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

การดำเนินการนี้จะตรวจสอบสิ่งต่างๆอย่างรวดเร็วเช่นว่าเป็นวัตถุเดียวกันหรือไม่และมีวัตถุอยู่หรือไม่ หลังจากนั้นจะทำการปรับขนาด / จัดสรรเพียงครั้งเดียวให้กับคำสั่งเป้าหมายจากนั้นคัดลอกองค์ประกอบทีละรายการ ฉันไม่เห็นว่าคุณเร็วกว่าในตัวcopy()มากนัก


1
ดูเหมือนว่าฉันควรเขียนโค้ดใหม่เพื่อหลีกเลี่ยงการใช้คำสั่งเลย - หรือใช้โครงสร้างข้อมูลที่เร็วกว่าซึ่งสามารถทำงานเดียวกันได้ ขอบคุณมากสำหรับคำตอบ!
Joern

56

บอกตรงๆว่า dict.copy เร็วกว่าอย่างที่บอก

[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

ขอบคุณสำหรับการเปรียบเทียบ! จะพยายามเขียนโค้ดใหม่เพื่อหลีกเลี่ยงการใช้การคัดลอก dict ในสถานที่ส่วนใหญ่ ขอบคุณอีกครั้ง!
Joern

4
วิธีที่จะทำการเปรียบเทียบที่ผ่านมาโดยไม่นับค่าใช้จ่ายในการดำเนินการนำเข้าทุกครั้งที่เป็นกับtimeit's อาร์กิวเมนต์:-s python -m timeit -s "from copy import copy" "new = copy({1:1, 2:2, 3:3})"ในขณะที่คุณทำอยู่ให้ดึงการสร้างตามคำสั่งออกมาด้วย (สำหรับตัวอย่างทั้งหมด)
Thomas Wouters

บางทีการทำซ้ำหลาย ๆ ครั้งจะดีกว่าเนื่องจากอาจมีความผันผวนของช็อตใดช็อตหนึ่ง
xiaohan2012

2
Timeit ไม่ว่า; อย่างที่บอกว่ามันวนซ้ำ 1000000 ครั้งและหาค่าเฉลี่ย
utdemir

ฉันมีเวลาที่ขัดแย้งกัน a = {b: b สำหรับ b ในช่วง (10000)} ใน [5]:% timeit copy (a) 10000 ลูปดีที่สุดคือ 3: 186 µs ต่อลูป In [6]:% timeit deepcopy (a) 100 loops, ดีที่สุด 3: 14.1 ms ต่อลูป In [7]:% timeit a.copy () 1,000 ลูปดีที่สุด 3: 180 วินาทีต่อลูป
Davoud Taghawi-Nejad

12

คุณสามารถให้ตัวอย่างโค้ดเพื่อที่ฉันจะได้เห็นว่าคุณใช้ copy () อย่างไรและในบริบทใด

คุณสามารถใช้

new = dict(old)

แต่ฉันไม่คิดว่ามันจะเร็วกว่านี้


5

ฉันรู้ว่านี่เป็นกระทู้เก่า แต่นี่เป็นผลลัพธ์ที่สูงในเครื่องมือค้นหา "dict copy python" และผลการค้นหาอันดับต้น ๆ สำหรับ "การคัดลอกแบบ dict" และฉันเชื่อว่าสิ่งนี้เกี่ยวข้อง

จาก Python 3.7 newDict = oldDict.copy()เร็วกว่าเดิมถึง 5.5 เท่า สะดุดตาตอนนี้newDict = dict(oldDict)ดูเหมือนว่าจะไม่มีการเพิ่มประสิทธิภาพนี้

มีข้อมูลน้อยมากขึ้นที่นี่


3

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

จากนั้น "สำเนา" จะเป็นพจนานุกรมที่ใช้ค้นหาสิ่งต่างๆในพจนานุกรม "หลัก" หากยังไม่มีคีย์ --- แต่จะมีการปรับเปลี่ยนเนื้อหาในตัวเอง

สิ่งนี้ถือว่าคุณจะไม่ได้แก้ไขต้นฉบับและการค้นหาเพิ่มเติมจะไม่ต้องเสียค่าใช้จ่ายมากขึ้น


2

การวัดขึ้นอยู่กับขนาดพจนานุกรม สำหรับสำเนา 10,000 รายการ (d) และ d.copy () เกือบจะเหมือนกัน

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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.