Я немного смущен тем, что можно / нельзя использовать в качестве ключа для Python dict.
dicked = {}
dicked[None] = 'foo' # None ok
dicked[(1,3)] = 'baz' # tuple ok
import sys
dicked[sys] = 'bar' # wow, even a module is ok !
dicked[(1,[3])] = 'qux' # oops, not allowed
Итак, кортеж - это неизменяемый тип, но если я спрячу в него список, то он не может быть ключом ... не мог бы я так же легко скрыть список внутри модуля?
У меня было смутное представление о том, что ключ должен быть «хешируемым», но я просто собираюсь признать свое незнание технических деталей; Я не знаю, что здесь происходит на самом деле. Что бы пошло не так, если бы вы попытались использовать списки в качестве ключей, с хешем, скажем, в качестве места в памяти?
Ответы:
В вики Python есть хорошая статья по этой теме: Почему списки не могут быть ключами словаря . Как объясняется там:
Это можно сделать, не нарушая никаких требований, но это приводит к неожиданному поведению. Списки обычно обрабатываются так, как если бы их значение было получено из значений их содержимого, например, при проверке (несоответствия) равенства. Многие, по понятным причинам, ожидали бы, что вы можете использовать любой список
[1, 2]
для получения одного и того же ключа, в то время как вам нужно будет оставить один и тот же объект списка. Но поиск по значению прерывается, как только список, используемый в качестве ключа, изменяется, а для поиска по идентификатору требуется, чтобы вы держали точно такой же список - что не требуется для любой другой общей операции со списком (по крайней мере, я не могу вспомнить ).Другие объекты, такие как модули
object
, в любом случае делают гораздо большую работу из своей идентичности объекта (когда в последний раз вы вызывали два разных объекта модуляsys
?), И все равно сравниваются с этим. Поэтому менее удивительно - или даже ожидалось - то, что они, при использовании в качестве ключей dict, в этом случае также сравниваются по идентичности.источник
Почему я не могу использовать список в качестве ключа dict в Python?
(для всех, кто сталкивается с этим вопросом и ищет способ его обойти)
как объясняют другие здесь, вы действительно не можете. Однако вы можете использовать его строковое представление, если действительно хотите использовать свой список.
источник
__eq__
. Но если вы конвертируете их в строки, все сравнивается по строковому представлению.Только что обнаружил, что вы можете преобразовать List в кортеж, а затем использовать его в качестве ключей.
источник
Проблема в том, что кортежи неизменяемы, а списки - нет. Рассмотрим следующие
Что должно
d[li]
вернуть? Это тот же список? Как насчетd[[1,2,3]]
? У него те же значения, но это другой список?В конечном счете, удовлетворительного ответа нет. Например, если работает только исходный ключ, то, если у вас нет ссылки на этот ключ, вы больше никогда не сможете получить доступ к значению. С любым другим разрешенным ключом вы можете создать ключ без ссылки на оригинал.
Если оба моих предложения работают, значит, у вас очень разные ключи, которые возвращают одно и то же значение, что более чем удивительно. Если работает только исходное содержимое, то ваш ключ быстро испортится, поскольку списки создаются для изменения.
источник
d[li]
что останусь. 5. Вd[[1,2,3]]
качестве ключа будет ссылаться на другой объект списка, так что это будет KeyError. Я пока не вижу никаких проблем ... кроме того, что если позволить ключу собирать мусор, это может сделать некоторые значения dict недоступными. Но это практическая проблема, а не логическая проблема ..d[list(li)]
наличие KeyError - часть проблемы. Почти каждый другой прецеденте ,li
будет неотличим от нового списка с одинаковым содержимым. Это работает, но многим это противоречит интуиции. Кроме того, когда в последний раз вам действительно приходилось использовать список в качестве ключа dict? Единственный вариант использования, который я могу себе представить, - это когда вы все равно хешируете все по идентичности, и в этом случае вы должны просто делать это, вместо того, чтобы полагаться на идентичность__hash__
и__eq__
быть на ее основе.Вот ответ http://wiki.python.org/moin/DictionaryKeys
Поиск разных списков с одинаковым содержимым даст разные результаты, даже если сравнение списков с одинаковым содержимым укажет на их эквивалент.
А как насчет использования литерала списка в поиске по словарю?
источник
Поскольку списки изменяемы,
dict
ключи (иset
члены) должны быть хешируемыми, а хеширование изменяемых объектов - плохая идея, поскольку хеш-значения должны вычисляться на основе атрибутов экземпляра.В этом ответе я приведу несколько конкретных примеров, которые, надеюсь, добавят ценности существующим ответам. Каждое понимание применимо также к элементам структуры данных
set
.Пример 1 : хеширование изменяемого объекта, где хеш-значение основано на изменяемой характеристике объекта.
После мутации
stupid
он больше не может быть найден в dict, потому что хеш изменился. Только линейное сканирование по списку ключей dict находитstupid
.Пример 2 : ... но почему не просто постоянное хеш-значение?
Это тоже не очень хорошая идея, потому что одинаковые объекты должны хешироваться одинаково, чтобы вы могли найти их в
dict
илиset
.Пример 3 : ... хорошо, а как насчет постоянных хешей во всех экземплярах ?!
Кажется, что все работает так, как ожидалось, но подумайте о том, что происходит: когда все экземпляры вашего класса производят одно и то же хеш-значение, у вас будет конфликт хешей всякий раз, когда имеется более двух экземпляров в качестве ключей в
dict
или присутствующих вset
.Для поиска подходящего экземпляра с помощью
my_dict[key]
orkey in my_dict
(илиitem in my_set
) необходимо выполнить столько проверок равенства, сколько экземпляровstupidlist3
в ключах dict (в худшем случае). На этом этапе цель словаря - поиск O (1) - полностью нарушена. Это продемонстрировано в следующих временных интервалах (сделано с IPython).Некоторые сроки для примера 3
Как видите, тест на членство в нашем тесте
stupidlists_set
даже медленнее, чем линейное сканирование в целомlists_list
, в то время как у вас есть ожидаемое сверхбыстрое время поиска (фактор 500) в наборе без множества хеш-коллизий.TL; DR: вы можете использовать их в
tuple(yourlist)
качествеdict
ключей, потому что кортежи неизменяемы и хешируются.источник
x
иz
то же самое. Если что-то неясно, задайте новый вопрос.hash(x)
иhash(z)
.Ваш тент можно найти здесь:
Источник и дополнительная информация: http://wiki.python.org/moin/DictionaryKeys
источник
Простой ответ на ваш вопрос заключается в том, что список классов не реализует хэш метода, который требуется для любого объекта, который желает использовать в качестве ключа в словаре. Однако причина, по которой хеш не реализован так же, как, скажем, в классе кортежей (на основе содержимого контейнера), заключается в том, что список является изменяемым, поэтому редактирование списка потребует пересчета хеша, что может означать список в теперь находится не в том сегменте в подчиненной хеш-таблице. Обратите внимание: поскольку вы не можете изменить кортеж (неизменяемый), он не сталкивается с этой проблемой.
В качестве примечания, фактическая реализация поиска диктобъектов основана на алгоритме D из Knuth Vol. 3, п. 6.4. Если у вас есть эта книга, ее стоит прочитать, кроме того, если вы действительно очень заинтересованы, вы можете взглянуть на комментарии разработчиков по поводу фактической реализации dictobject здесь. Он очень подробно описывает, как именно это работает. Существует также лекция на Python о реализации словарей, которая может вас заинтересовать. В течение первых нескольких минут они проходят через определение ключа и того, что такое хэш.
источник
Согласно документации Python 2.7.2:
Кортеж неизменен в том смысле, что вы не можете добавлять, удалять или заменять его элементы, но сами элементы могут быть изменяемыми. Хеш-значение списка зависит от хеш-значений его элементов, поэтому оно изменяется при изменении элементов.
Использование идентификаторов для хэшей списков означало бы, что все списки сравниваются по-разному, что было бы удивительно и неудобно.
источник
hash = id
не нарушает инвариант в конце первого абзаца, вопрос в том, почему так не делается.что-то вроде (псевдо-код):
Если вам интересно, какие из доступных опций можно использовать в качестве ключа для вашего словаря. затем
Можешь попробовать :
Если он работает нормально, его можно использовать в качестве ключа для вашего словаря или преобразовать во что-то хешируемое.
Коротко :
tuple(<your list>)
.str(<your list>)
.источник
dict
ключи должны быть хешируемыми. Списки изменяемы и не предоставляют допустимого метода хеширования .источник