Рубрики
Без рубрики

Обновите свои навыки Python: изучение словаря

Автор оригинала: FreeCodeCapm Team.

Адамом Голдшмидтом

Если пахнет как Python Диктовать чувствует себя как Диктовать и выглядит как один … ну, это должно быть Диктовать Отказ Абсолютно! Ох, а Установить тоже…

Хм?

Словари и наборы в Python реализуются с помощью хеш-таблица. Сначала это может звучать пугающим, но, поскольку мы расследуем дальше, все должно быть понятно.

Цель

На протяжении всей этой статьи мы узнаем, как Диктовать реализован в Python, и мы создадим нашу собственную реализацию (простой) один. Статья разделена на три части, а на нашем пользовательском словаре происходит в первые два:

  1. Понимание того, какие хеш-таблицы есть и как их использовать
  2. Дайвинг в исходный код Python, чтобы лучше понять, как реализованы словари
  3. Изучение различий между словарем и другими структурами данных, такими как списки и наборы

Что такое хэш-стол?

Хэш-таблица – это структура, которая предназначена для хранения списка пар клавишных пар, без ущерба для скорости и эффективности манипулирования и поиска структуры.

Эффективность хеш-стола получена из хеш-функция – Функция, которая вычисляет индекс пары ключа-значения – означает, что мы можем быстро вставить, поискать и удалять элементы, поскольку мы знаем их индекс в массиве памяти.

Сложность начинается, когда два наших ключа хэш на то же значение. Этот сценарий называется хэш столкновение Отказ Существует много разных способов обработки столкновения, но мы будем только покрывать путь Питона. Мы не будем слишком глубоко с нашим объяснением хэш-стола ради сохранения этой статьи на новичке и ориентированы на питоне.

Давайте убедимся, что мы завернули голову вокруг концепции хэш-таблиц, прежде чем двигаться дальше. Мы начнем с создания скелетов для нашего очень (очень) простого пользовательского Диктовать Состоит из только методов вставки и поиска, используя некоторые из Python’s Методы гуляния Отказ Нам нужно будет инициатировать таблицу HASH со списком определенного размера и включить подписку ([] знак) для него:

Теперь наш список хеш-таблицы должен удерживать определенные структуры, каждый из которых содержит ключ, значение и хеш:

Основной пример

Небольшая компания с 10 сотрудниками хотят вести учет, содержащие их сотрудники, остальные больные дни. Мы можем использовать следующую функцию хэша, поэтому все может поместить в массиве памяти:

Длина имени сотрудника% Tate_size

Давайте определим нашу функцию хешей в классе входа:

Теперь мы можем инициализировать 10 элементных массив в нашем столе:

Ждать! Давайте подумаем об этом. Мы, скорее всего, будем решать некоторые хэш-столкновения. Если у нас есть только 10 элементов, нам будет гораздо сложнее найти открытое пространство после столкновения. Давайте решим, что наш стол будет удвоить размер – 20 элементов! Это придет удобно в будущем, обещаю.

Чтобы быстро вставить каждого работника, мы будем следовать логике:

array[length of the employee's name % 20] = employee_remaining_sick_days

Таким образом, наш метод вставки будет выглядеть следующее (еще не обрабатывающее хеш-столкновение):

Для поиска мы в основном делаем то же самое:

array[length of the employee's first name % 20] 

Мы еще не закончены!

Обработка столкновения Python

Python использует метод, называемый открытым адресом для обработки столкновений. Это также изменяет размеры хэш, когда он достигает определенного размера, но мы не будем обсуждать этот аспект. Определение адресации от Википедия :

Давайте рассмотрим процесс извлечения значения по ключ , глядя на Python Исходный код (написано в C):

  1. Рассчитать хэш ключ
  2. Рассчитать индекс товара по Hash & Mask где Маска-1 (Простые условия – принять последние биты из хеш-битов):
i = (size_t)hash & mask;

3. Если пусто, верните Dkix_empty который в конечном итоге переводится на KeyError :

if (ix == DKIX_EMPTY) {   *value_addr = NULL;   return ix;}

4. Если не пусто, сравните клавиши и хэши и набор value_addr обратитесь к фактическому адресу значений, если равное:

if (ep->me_key == key) {    *value_addr = ep->me_value;    return ix;}

а также:

if (dk == mp->ma_keys && ep->me_key == startkey) {    if (cmp > 0) {        *value_addr = ep->me_value;        return ix;    }}

5. Если не равны, используйте разные биты хэши (алгоритм объяснил здесь ) и снова перейдите к шагу 3:

perturb >>= PERTURB_SHIFT;i = (i*5 + perturb + 1) & mask;

Вот диаграмма для иллюстрации всего процесса:

Процесс вставки довольно похож – если обнаруженный слот пуст, вход вставляется, если она не пусто, то мы сравниваем ключ и хеш – если равны, мы заменяем значение, и если нет, мы продолжим наш квест находки Новое место с Востура алгоритм.

Идеи заимствования из Python

Мы можем занимать идею Python по сравнению как ключей и хэши каждой записи к нашему объекту ввода (замена предыдущего метода):

Наш хеш-столик до сих пор не имеет никакого обращения с столкновением – давайте реализовать один! Как мы видели ранее, Python делает это, сравнивая записи, а затем изменив маску битов, но мы сделаем это с помощью метода, называемого линейным зондированием (что является формой открытой адресации, объясненной выше):

Так что мы собираемся сделать, это двигаться вперед, пока мы не найдем открытое пространство. Если вы вспомните, мы реализовали наш стол с двойным размером (20 элементов и не 10) – Это где это приходит удобно Отказ Когда мы движемся вперед, наш поиск открытого пространства будет намного быстрее, потому что есть больше места!

Но у нас есть проблема. Что если кому-то зло пытается вставить 11-й элемент? Нам нужно поднять ошибку (мы не будем иметь дело с изменением таблицы в этой статье). Мы можем держать счетчик заполненных записей в нашем столе:

Теперь давайте реализуем то же самое в нашем методе поиска:

Полный код можно найти здесь .

Теперь компания может безопасно хранить больные дни для каждого сотрудника:

Набор Python

Возвращаясь к началу статьи, Установить и Диктовать В Python реализован очень похожи, с Установить Используя только ключ и хеш Внутри каждой записи, как можно увидеть в Исходный код :

typedef struct {    PyObject *key;    Py_hash_t hash; /* Cached hash code of the key */} setentry;

В отличие от Диктовать , это имеет значение:

typedef struct {    /* Cached hash code of me_key. */    Py_hash_t me_hash;    PyObject *me_key;    PyObject *me_value; /* This field is only meaningful for combined tables */} PyDictKeyEntry;

Производительность и заказ

Сравнение времени

Я думаю, теперь сейчас ясно, что Диктовать намного быстрее, чем Список (и наносит больше места памяти), с точки зрения поиска, вставки (в определенном месте) и удалением. Давайте проверим это предположение с некоторым кодом (я запускаю код на MacBook 2017 Pro):

И Ниже приведен тестовый код (один раз для Dict и один раз для Список , замена d ):

Результаты, ну, значительно, что мы ожидали.

Диктовать : 0.015382766723632812 секунды

Список: 55.5544171333313 секунды

Заказ зависит от заказа ввода

Орден Диктования зависит от истории вставки. Если мы вставем запись с определенным хеш, и после этого вход с той же хэш, вторая запись будет в конечном итоге в другом месте, тогда если мы должны были вставить его первым.

Перед тем, как ты уйдешь…

Спасибо за прочтение! Вы можете следовать за мной на Средний Для получения дополнительной части этих статей или на Github Для открытия каких-то крутых REPOS:)

Если вам понравилось эту статью, пожалуйста, удерживайте кнопку CLAP? чтобы помочь другим найти это. Чем дольше вы держите его, тем больше хлопья, которые вы даете!

И не стесняйтесь делиться своими мыслями в комментариях ниже, или поправьте меня, если я что-то не так.

Дополнительные ресурсы

  1. Hash Crash: Основы хэш-таблиц
  2. Могучий словарь
  3. Введение в алгоритмы