Почему ключи словаря должны быть неизменяемыми?

Почему необходимо, чтобы ключи словаря были неизменяемыми? Я ищу простую, ясную причину, почему ключи в словарях Python имеют это ограничение.

2 ответов


на моем компьютере есть файл /etc/dictionaries-common/words содержащий большую коллекцию английских слов:

>>> with open("/etc/dictionaries-common/words") as f:
...     words = [line.strip() for line in f]
... 
>>> "python" in words
True
>>> "BDFL" in words
False

давайте создадим словарь, хранящий длины всех этих слов:

>>> word_lengths = {w: len(w) for w in words}
>>> word_lengths["parrot"]
6

и, просто для удовольствия, мы перетасуем наш оригинальный список слов:

>>> from random import shuffle
>>> shuffle(words)
>>> words[:5]
["Willie's", 'Araceli', 'accessed', 'engagingly', 'hobnobs']

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

>>> all(w in words for w in word_lengths)
True
>>> len(words)
99171

... почти сто тысяч слов для проверки, и для каждого из них в словарь, Python имеет для поиска в списке слов пока не найдет совпадение. Он не всегда будет проверять весь список, но в среднем это будет пятьдесят тысяч слов (или половина списка) каждый раз, в общей сложности 50,000 × 100,000 = 5,000,000,000 тестов. Пять миллиардов-это много, даже в наш чудесный век технологий.

чтобы быть абсолютно уверенным (обычно я не такой параноик, обычно я просто засыпаю), давайте проверим, наоборот, и убедиться, что все в words еще в word_lengths:

>>> all(w in word_lengths for w in words)
True

Эй, что? На этот раз это была десятая доля секунды! Что происходит? Ты меня пугаешь, чувак ... а где мои бисквиты? Они у меня только что были, я уверен.

в отличие от списка, который может быть в любом старом порядке (поэтому убедитесь, что какой-то элемент там означает проверку каждого элемента по очереди, пока мы его не найдем), словарь немного более эффективен. Наверное, меньше веселья на вечеринках, но эй, оставьте его за музыку, и все будет в порядке, знаешь?

секрет безжалостной эффективности словарей заключается в том, что для каждого элемента словарь вычисляет хэш (просто целое число, на самом деле) ключа на основе его содержимого и использует его для хранения элемента в определенном месте в памяти. Затем, когда вы идете искать элемент, он снова вычисляет хэш содержимого ключа, говорит себе: "хорошо,"python", что хэши в 7036520087640895475 ... да, я знаю, куда я должен был положить это, тогда", и идет прямо к правильной памяти место, чтобы найти его. Так что на этот раз ему нужно было сделать только сто тысяч чеков, а не пять миллиардов.

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

но есть цена, чтобы заплатить за способность словари, чтобы держать его вместе. Помните, когда я сказал, что словарь вычисляет хэш на основе содержимого элемента? А что будет, если содержание изменится? Для неизменяемых объектов это не проблема - их содержание не могу change - но изменяемые объекты, по определению,можете измените их содержимое, и когда они это сделают, их хэш (если он у них даже есть) тоже изменится. Это круто, очевидно, не все хотят, чтобы их положили в коробку, я понимаю, но если хэш изменился, словарь не сможет понять, куда он положил эту вещь.

это как будто Joy Division изменили свое название на New Order, и теперь вы понятия не имеете, где вы разместили этот 12-дюймовый ремикс Blue Monday. Это просто не сработает.

Итак, словари имеют правило: если вы хотите быть ключом, не меняй.


Как Фредрик Lundhбыл здесь:

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