Telegram Group & Telegram Channel
Как реализован dict в интерпретаторе Python

Словарь (ассоциативный массив / хэш-таблица / dict) — это такая замечательная структура данных в Python, которая поддерживает обращение, вставку и удаление в среднем за константное время O(1).

Возьмем в качестве примера словарь, который отображает реальное имя человека в его игровой никнейм.

nickname = {
"Gena": "Машина",
"Ivan": 12,
"Vazgen": "Gorniy Orel 228"
}


Визуально очень похоже на JSON. Ключами выступают "Gena", "Ivan", значениями "Машина", 12 и тд. Причем последние могут быть чем угодно: объектами, числами, строками, списками, словарями.

Ключи же должны быть хэшируемыми типами данных — такими, у которых определен метод __hash__. Обычно эти объекты являются неизменяемыми. Т.е. tuple может выступать в качестве ключа, а list — нет. Хотя в пользовательских классах можно наворотить все что угодно.

Что происходит на уровне интерпретатора CPython при добавлении нового ключа? Например, мы выполнили строчку nickname["Dima"] = "Арчибальд Шпицен-Дроссель".

На уровне C есть просто два массива фиксированной длины: dk_indices — хранит индексы и dk_entries — хранит пары ключ-значение, плюс несколько вспомогательных полей с размером самого словаря, типами данных и тд.

При вставке для объекта "Dima" вызывается функция PyObject_Hash, которая залезает в PyTypeObject — структуру с основными свойствами объекта (в Python все есть объект) и находит там хэш-функцию unicode_hash (строки из примера под капотом unicode). Применяя ее к "Dima", получает некоторое большое по модулю число. Кстати, сложность хэширования O(k), но мы предполагаем, что длина ключей в среднем сильно меньше размера словаря k << n.

Чтобы найти слот для hash("Dima") в dk_indices, берется остаток от деления на длину dk_indices. Если соответствующая ячейка в массиве пуста, то в dk_entries добавляется пара ("Dima", "Арчибальд Шпицен-Дроссель"). Если же ячейка занята, это означает что произошла коллизия. В простейшем случае можно было бы хранить в dk_entries не просто ключ-значение, а связный список из ключей и значений. Тогда при коллизиях мы бы просто делали append в конец списка.

Но в CPython используется алгоритм сдвига с пертурбацией. В случае коллизии он вычисляет новый индекс по формуле
    perturb >>= PERTURB_SHIFT;
j = (5*j) + 1 + perturb;
use j % 2**i as the next table index;

И повторяет процедуру до тех пор, пока не найдет свободную ячейку.

Такие дела. Все вышеперечисленное не претендует на истину в последней инстанции. Там еще есть PyDictKeyEntry vs PyDictUnicodeEntry, 6000 строк реализации dictobject.c и куча прочих нюансов. Но основная идея такая, если я нигде не наврал. А если наврал, пишите замечания в комментариях!



group-telegram.com/savostyanov_dmitry/491
Create:
Last Update:

Как реализован dict в интерпретаторе Python

Словарь (ассоциативный массив / хэш-таблица / dict) — это такая замечательная структура данных в Python, которая поддерживает обращение, вставку и удаление в среднем за константное время O(1).

Возьмем в качестве примера словарь, который отображает реальное имя человека в его игровой никнейм.

nickname = {
"Gena": "Машина",
"Ivan": 12,
"Vazgen": "Gorniy Orel 228"
}


Визуально очень похоже на JSON. Ключами выступают "Gena", "Ivan", значениями "Машина", 12 и тд. Причем последние могут быть чем угодно: объектами, числами, строками, списками, словарями.

Ключи же должны быть хэшируемыми типами данных — такими, у которых определен метод __hash__. Обычно эти объекты являются неизменяемыми. Т.е. tuple может выступать в качестве ключа, а list — нет. Хотя в пользовательских классах можно наворотить все что угодно.

Что происходит на уровне интерпретатора CPython при добавлении нового ключа? Например, мы выполнили строчку nickname["Dima"] = "Арчибальд Шпицен-Дроссель".

На уровне C есть просто два массива фиксированной длины: dk_indices — хранит индексы и dk_entries — хранит пары ключ-значение, плюс несколько вспомогательных полей с размером самого словаря, типами данных и тд.

При вставке для объекта "Dima" вызывается функция PyObject_Hash, которая залезает в PyTypeObject — структуру с основными свойствами объекта (в Python все есть объект) и находит там хэш-функцию unicode_hash (строки из примера под капотом unicode). Применяя ее к "Dima", получает некоторое большое по модулю число. Кстати, сложность хэширования O(k), но мы предполагаем, что длина ключей в среднем сильно меньше размера словаря k << n.

Чтобы найти слот для hash("Dima") в dk_indices, берется остаток от деления на длину dk_indices. Если соответствующая ячейка в массиве пуста, то в dk_entries добавляется пара ("Dima", "Арчибальд Шпицен-Дроссель"). Если же ячейка занята, это означает что произошла коллизия. В простейшем случае можно было бы хранить в dk_entries не просто ключ-значение, а связный список из ключей и значений. Тогда при коллизиях мы бы просто делали append в конец списка.

Но в CPython используется алгоритм сдвига с пертурбацией. В случае коллизии он вычисляет новый индекс по формуле
    perturb >>= PERTURB_SHIFT;
j = (5*j) + 1 + perturb;
use j % 2**i as the next table index;

И повторяет процедуру до тех пор, пока не найдет свободную ячейку.

Такие дела. Все вышеперечисленное не претендует на истину в последней инстанции. Там еще есть PyDictKeyEntry vs PyDictUnicodeEntry, 6000 строк реализации dictobject.c и куча прочих нюансов. Но основная идея такая, если я нигде не наврал. А если наврал, пишите замечания в комментариях!

BY Дмитрий Савостьянов Вещает


Warning: Undefined variable $i in /var/www/group-telegram/post.php on line 260

Share with your friend now:
group-telegram.com/savostyanov_dmitry/491

View MORE
Open in Telegram


Telegram | DID YOU KNOW?

Date: |

The picture was mixed overseas. Hong Kong’s Hang Seng Index fell 1.6%, under pressure from U.S. regulatory scrutiny on New York-listed Chinese companies. Stocks were more buoyant in Europe, where Frankfurt’s DAX surged 1.4%. Some privacy experts say Telegram is not secure enough These administrators had built substantial positions in these scrips prior to the circulation of recommendations and offloaded their positions subsequent to rise in price of these scrips, making significant profits at the expense of unsuspecting investors, Sebi noted. The regulator said it has been undertaking several campaigns to educate the investors to be vigilant while taking investment decisions based on stock tips. Stocks dropped on Friday afternoon, as gains made earlier in the day on hopes for diplomatic progress between Russia and Ukraine turned to losses. Technology stocks were hit particularly hard by higher bond yields.
from id


Telegram Дмитрий Савостьянов Вещает
FROM American