Швидкий спосіб копіювання словника на Python


92

У мене є програма Python, яка багато працює зі словниками. Я повинен робити копії словників тисячі разів. Мені потрібна копія ключів та відповідного вмісту. Копія буде відредагована і не повинна бути пов’язана з оригіналом (наприклад, зміни в копії не повинні впливати на оригінал.)

Клавіші - це рядки, значення - цілі числа (0/1).

В даний час я використовую простий спосіб:

newDict = oldDict.copy()

Профілювання мого коду показує, що операція копіювання займає більшу частину часу.

Чи є швидші альтернативи dict.copy()методу? Що було б найшвидшим?


1
Якщо значення може бути 0 або 1, чи boolбуде кращим вибором, ніж int?
Самір Талвар,

5
І якщо вам потрібні тисячі їх примірників, чи працюють бітові маски ще краще?
Вубл

@Samir все одно не boolв Python int.
Санта

Однак я погоджуюсь, що бітова маска може бути для вас більш ефективною (залежно від того, як ви справді використовуєте цей "dict").
Санта

1
Для уточнення, boolтип насправді є підкласом (підтипом?) intТипу.
Санта

Відповіді:


64

Переглядаючи джерело C для dictоперацій Python , ви можете побачити, що вони роблять досить наївну (але ефективну) копію. По суті, це зводиться до дзвінка PyDict_Merge:

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

Це робить швидкі перевірки на наявність таких речей, як, якщо це один і той самий об’єкт і чи є в них об’єкти. Після цього він робить щедру одноразову зміну розміру / розміщення цільового дикту, а потім копіює елементи по одному. Я не бачу, що ти стаєш набагато швидшим за вбудований copy().


1
Здається, мені краще переписати код, щоб взагалі не використовувати дикти - або використовувати швидшу структуру даних, яка може виконувати ту саму роботу. Щиро дякую за відповідь!
Йоерн,

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

Дякую за порівняння! Спробує переписати код, щоб уникнути використання копіювання диктів у більшості місць. Знову дякую!
Йоерн,

4
Спосіб зробити останнє порівняння без підрахунку вартості кожного разу зробити імпорт - це аргумент timeit' -s: python -m timeit -s "from copy import copy" "new = copy({1:1, 2:2, 3:3})". Поки ви це робите, також витягніть створення дикту (для всіх прикладів)
Томас Вутерс,

Можливо, краще повторити процеси багато разів, оскільки можуть бути коливання одного конкретного знімка.
xiaohan2012

2
Timeit робить це; як там сказано, він цикл 1000000 разів і усереднює його.
utdemir

У мене суперечливі терміни. a = {b: b для b в діапазоні (10000)} В [5]:% timeit copy (a) 10000 циклів, найкраще 3: 186 µs на петлю In [6]:% timeit deepcopy (a) 100 циклів, найкраще з 3: 14,1 мс на цикл У [7]:% timeit a.copy () 1000 циклів, найкраще з 3: 180 мкс на цикл
Давуд Таґаві-Неджад,

12

Чи можете ви надати зразок коду, щоб я міг побачити, як ви використовуєте copy () і в якому контексті?

Ви могли б використовувати

new = dict(old)

Але я не думаю, що це буде швидше.


5

Я усвідомлюю, що це стара тема, але це високий результат у пошукових системах для "dict copy python" та найкращий результат "dict copy performance", і я вважаю, що це актуально.

Від Python 3.7 newDict = oldDict.copy()до 5,5 разів швидше, ніж було раніше. Примітно, що зараз, newDict = dict(oldDict)схоже, цього зростання продуктивності не спостерігається.

Існує трохи більше інформації тут .


3

Залежно від того, що ви залишаєте для спекуляцій, можливо, ви захочете обернути оригінальний словник та зробити своєрідне копіювання на запис.

"Копія" - це тоді словник, який шукає речі у "батьківському" словнику, якщо він ще не містить ключа ---, але модифікації наповнює сам по собі.

Це передбачає, що ви не будете модифікувати оригінал і що додаткові пошуки в підсумку не будуть коштувати дорожче.


2

Вимірювання залежать від розміру словника. Для 10000 записів copy (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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.