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

"We as Ukrainians believe that the truth is on our side, whether it's truth that you're proclaiming about the war and everything else, why would you want to hide it?," he said. Since its launch in 2013, Telegram has grown from a simple messaging app to a broadcast network. Its user base isn’t as vast as WhatsApp’s, and its broadcast platform is a fraction the size of Twitter, but it’s nonetheless showing its use. While Telegram has been embroiled in controversy for much of its life, it has become a vital source of communication during the invasion of Ukraine. But, if all of this is new to you, let us explain, dear friends, what on Earth a Telegram is meant to be, and why you should, or should not, need to care. Oleksandra Matviichuk, a Kyiv-based lawyer and head of the Center for Civil Liberties, called Durov’s position "very weak," and urged concrete improvements. In addition, Telegram's architecture limits the ability to slow the spread of false information: the lack of a central public feed, and the fact that comments are easily disabled in channels, reduce the space for public pushback. A Russian Telegram channel with over 700,000 followers is spreading disinformation about Russia's invasion of Ukraine under the guise of providing "objective information" and fact-checking fake news. Its influence extends beyond the platform, with major Russian publications, government officials, and journalists citing the page's posts.
from ua


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