Для целей кэширования мне нужно сгенерировать ключ кеша из аргументов GET, которые присутствуют в dict.
В настоящее время я использую sha1(repr(sorted(my_dict.items())))
( sha1()
это удобный метод, который использует hashlib внутри), но мне интересно, есть ли лучший способ.
python
hash
dictionary
ThiefMaster
источник
источник
{'a': 1, 'b':2}
семантически так же, как{'b':2, 'a':1}
). Я еще не использовал его для чего-то слишком сложного, так что YMMV, но обратная связь приветствуется.Ответы:
Если ваш словарь не является вложенным, вы можете создать морозильник с элементами dict и использовать
hash()
:Это гораздо менее вычислительно, чем генерация строки JSON или представление словаря.
ОБНОВЛЕНИЕ: Пожалуйста, смотрите комментарии ниже, почему этот подход не может дать стабильный результат.
источник
hash()
функция не выдает стабильный вывод. Это означает, что при одинаковом вводе он возвращает разные результаты с разными экземплярами одного и того же интерпретатора python. Мне кажется, что при каждом запуске интерпретатора генерируется какое-то начальное значение.Использование
sorted(d.items())
недостаточно, чтобы получить стабильный репр. Некоторые значенияd
могут быть словарями, и их ключи будут по-прежнему отображаться в произвольном порядке. Пока все ключи являются строками, я предпочитаю использовать:Тем не менее, если хэши должны быть стабильными на разных машинах или версиях Python, я не уверен, что это пуленепробиваемый. Вы можете добавить
separators
иensure_ascii
аргументы , чтобы защитить себя от каких - либо изменений по умолчанию там. Буду признателен за комментарии.источник
ensure_ascii
Аргумент будет защищать против этого совершенно гипотетической проблемы.make_hash
. gist.github.com/charlax/b8731de51d2ea86c6eb9default=str
вdumps
команду. Кажется, работает хорошо.РЕДАКТИРОВАТЬ : Если все ваши ключи являются строками , то прежде чем продолжить читать этот ответ, пожалуйста, посмотрите значительно более простое (и более быстрое) решение Джека О'Коннора (которое также работает для хэширования вложенных словарей).
Хотя ответ принят, заголовок вопроса - «Хеширование словаря питона», и в отношении этого заголовка ответ неполон. (Что касается основной части вопроса, ответ полный.)
Вложенные словари
Если кто-то ищет в Stack Overflow способ хэширования словаря, он может наткнуться на этот метко озаглавленный вопрос и оставить неудовлетворенным, если кто-то пытается хэшировать несколько вложенных словарей. Ответ выше не сработает в этом случае, и вам придется реализовать какой-то рекурсивный механизм для извлечения хеша.
Вот один из таких механизмов:
Бонус: хеширование объектов и классов
hash()
Функция отлично работает , когда вы хэширования классы или экземпляры. Тем не менее, вот одна проблема, которую я нашел с хэшем, что касается объектов:Хэш остается тем же, даже после того, как я изменил foo. Это связано с тем, что идентичность foo не изменилась, поэтому хеш остается тем же. Если вы хотите, чтобы foo хэшировал по-разному в зависимости от его текущего определения, решение состоит в том, чтобы хэшировать все, что на самом деле меняется. В этом случае
__dict__
атрибут:Увы, когда вы пытаетесь сделать то же самое с самим классом:
Свойство класса
__dict__
не является обычным словарем:Вот механизм, аналогичный предыдущему, который будет обрабатывать классы соответствующим образом:
Вы можете использовать это, чтобы вернуть кортеж хэша, сколько угодно элементов:
ПРИМЕЧАНИЕ: весь приведенный выше код предполагает использование Python 3.x. Не тестировал в более ранних версиях, хотя я предполагаю, что
make_hash()
будет работать, скажем, в 2.7.2. Что касается делает работу примеров, я действительно знаю , чтоследует заменить на
источник
hash
вокруг списков и кортежей. В противном случае он берет мои списки целых чисел, которые оказались значениями в моем словаре, и возвращает обратно списки хэшей, а это не то, что я хочу.Вот более четкое решение.
источник
if isinstance(o,list):
на,if isinstance(obj, (set, tuple, list)):
то эта функция может работать на любом объекте.Приведенный ниже код избегает использования хэш-функции Python, поскольку она не будет предоставлять хеш-коды, которые являются согласованными при перезапусках Python (см. Хеш-функция в Python 3.3 возвращает разные результаты между сессиями ).
make_hashable()
преобразует объект во вложенные кортежи, аmake_hash_sha256()
также преобразует вrepr()
хеш SHA256 в кодировке base64.источник
make_hash_sha256(((0,1),(2,3)))==make_hash_sha256({0:1,2:3})==make_hash_sha256({2:3,0:1})!=make_hash_sha256(((2,3),(0,1)))
, Это не совсем то решение, которое я ищу, но это хорошее промежуточное звено. Я думаю о добавленииtype(o).__name__
в начало каждого из кортежей, чтобы вызвать дифференциацию.tuple(sorted((make_hashable(e) for e in o)))
Обновлено с 2013 года ответить ...
Ни один из приведенных выше ответов не кажется мне надежным. Причиной является использование предметов (). Насколько я знаю, это происходит в машинно-зависимом порядке.
Как насчет этого вместо этого?
источник
dict.items
не возвращать предсказуемо упорядоченный список?frozenset
позаботится об этомhash
не заботится о том, как распечатывается содержимое frozenset или что-то в этом роде. Протестируйте его на нескольких машинах и версиях Python, и вы увидите.Чтобы сохранить порядок ключей, вместо
hash(str(dictionary))
илиhash(json.dumps(dictionary))
я предпочел бы быстрое и грязное решение:Это будет работать даже для типов, подобных
DateTime
и более, которые не сериализуемы в JSON.источник
Вы могли бы использовать сторонний
frozendict
модуль, чтобы заморозить ваш диктант и сделать его хэшируемым.Для обработки вложенных объектов вы можете использовать:
Если вы хотите поддерживать больше типов, используйте
functools.singledispatch
(Python 3.7):источник
dict
изDataFrame
объектов.elif
предложение, чтобы оно работало сDataFrame
s:elif isinstance(x, pd.DataFrame): return make_hashable(hash_pandas_object(x).tolist())
я отредактирую ответ и посмотрю, принимаете ли вы его ...hash
рандомизация - это преднамеренная функция безопасности, включенная по умолчанию в python 3.7.Вы можете использовать библиотеку карт, чтобы сделать это. В частности, карты. FrozenMap
Чтобы установить
maps
, просто сделайте:Он также обрабатывает вложенный
dict
случай:Отказ от ответственности: я автор
maps
библиотеки.источник
.recurse
. См. Maps.readthedocs.io/en/latest/api.html#maps.FrozenMap.recurse . Порядок в списках имеет смысл с семантической точки зрения, если вы хотите независимость порядка, вы можете преобразовать свои списки в наборы до вызова.recurse
. Вы также можете использоватьlist_fn
параметр, чтобы.recurse
использовать другую хешируемую структуру данных, чемtuple
(.egfrozenset
)Один из способов решения этой проблемы - создать кортеж из словаря:
источник
Я делаю это так:
источник
hash(str({'a': 1, 'b': 2})) != hash(str({'b': 2, 'a': 1}))
(хотя это может работать для некоторых словарей, но не гарантируется , что оно будет работать для всех).