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: |

Two days after Russia invaded Ukraine, an account on the Telegram messaging platform posing as President Volodymyr Zelenskiy urged his armed forces to surrender. And while money initially moved into stocks in the morning, capital moved out of safe-haven assets. The price of the 10-year Treasury note fell Friday, sending its yield up to 2% from a March closing low of 1.73%. 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. In December 2021, Sebi officials had conducted a search and seizure operation at the premises of certain persons carrying out similar manipulative activities through Telegram channels. These entities are reportedly operating nine Telegram channels with more than five million subscribers to whom they were making recommendations on selected listed scrips. Such recommendations induced the investors to deal in the said scrips, thereby creating artificial volume and price rise.
from tw


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