9 ответов:
Если ваш словарь не вложен, вы можете сделать frozenset с элементами диктатора и использовать
hash():hash(frozenset(my_dict.items()))это гораздо менее вычислительно интенсивно, чем создание строки JSON или представления словаря.
используя
sorted(d.items())недостаточно, чтобы получить стабильный repr. Некоторые значения вdтакже могут быть словари, и их ключи все равно будут выходить в произвольном порядке. Пока все ключи являются строками, я предпочитаю использовать:json.dumps(d, sort_keys=True)тем не менее, если хэши должны быть стабильными на разных машинах или версиях Python, я не уверен, что это пуленепробиваемый. Возможно, вы захотите добавить
separatorsиensure_asciiаргументы, чтобы защитить себя от любых изменений там по умолчанию. Я был бы признателен за комментарии.
EDIT: если все ваши ключи строки, то прежде чем продолжить читать этот ответ, Пожалуйста, смотрите Джек О'Коннор значительно более простое (и быстрое) решение (который также работает для перемешивания вложенных словарей).
хотя ответ был принят, название вопроса "хеширование словаря Python", и ответ является неполным в отношении этого наименования. (Что касается тела вопроса, ответ таков полный.)
Вложенные Словари
если вы ищете переполнение стека для того, как хэшировать словарь, можно наткнуться на этот метко названный вопрос и оставить неудовлетворенным, если вы пытаетесь хэшировать несколько вложенных словарей. Ответ выше не будет работать в этом случае, и вам придется реализовать какой-то рекурсивный механизм для извлечения хэша.
вот один такой механизм:
import copy def make_hash(o): """ Makes a hash from a dictionary, list, tuple or set to any level, that contains only other hashable types (including any lists, tuples, sets, and dictionaries). """ if isinstance(o, (set, tuple, list)): return tuple([make_hash(e) for e in o]) elif not isinstance(o, dict): return hash(o) new_o = copy.deepcopy(o) for k, v in new_o.items(): new_o[k] = make_hash(v) return hash(tuple(frozenset(sorted(new_o.items()))))бонус: Хеширование объектов и классов
функция hash () отлично работает, когда вы хэш-классы или экземпляры. Однако вот одна проблема, которую я нашел с хэшем, что касается объектов:
class Foo(object): pass foo = Foo() print (hash(foo)) # 1209812346789 foo.a = 1 print (hash(foo)) # 1209812346789хэш такой же, даже после того, как я изменил foo. Это потому, что идентичность foo не изменилась, поэтому хэш-это то же самое. Если вы хотите Foo, чтобы окрошка по-разному в зависимости от его текущего определения, решение заключается в хэш все, что на самом деле меняется. В этом случае __дикт__ атрибутов:
class Foo(object): pass foo = Foo() print (make_hash(foo.__dict__)) # 1209812346789 foo.a = 1 print (make_hash(foo.__dict__)) # -78956430974785увы, при попытке сделать то же самое с самим классом:
print (make_hash(Foo.__dict__)) # TypeError: unhashable type: 'dict_proxy'свойство class _ _ dict__ не является обычным словарем:
print (type(Foo.__dict__)) # type <'dict_proxy'>вот такой же механизм, как и предыдущий, который будет обрабатывать классы соответствующим образом:
import copy DictProxyType = type(object.__dict__) def make_hash(o): """ Makes a hash from a dictionary, list, tuple or set to any level, that contains only other hashable types (including any lists, tuples, sets, and dictionaries). In the case where other kinds of objects (like classes) need to be hashed, pass in a collection of object attributes that are pertinent. For example, a class can be hashed in this fashion: make_hash([cls.__dict__, cls.__name__]) A function can be hashed like so: make_hash([fn.__dict__, fn.__code__]) """ if type(o) == DictProxyType: o2 = {} for k, v in o.items(): if not k.startswith("__"): o2[k] = v o = o2 if isinstance(o, (set, tuple, list)): return tuple([make_hash(e) for e in o]) elif not isinstance(o, dict): return hash(o) new_o = copy.deepcopy(o) for k, v in new_o.items(): new_o[k] = make_hash(v) return hash(tuple(frozenset(sorted(new_o.items()))))вы можете использовать это, чтобы вернуть хэш-кортеж из любого количества элементов, которые вы хотите:
# -7666086133114527897 print (make_hash(func.__code__)) # (-7666086133114527897, 3527539) print (make_hash([func.__code__, func.__dict__])) # (-7666086133114527897, 3527539, -509551383349783210) print (make_hash([func.__code__, func.__dict__, func.__name__]))Примечание: весь приведенный выше код предполагает Python 3.х. Не тест в более ранних версиях, хотя я предполагаю, что make_hash() будет работать, скажем, 2.7.2. Что касается того, чтобы примеры работали, я do известно, что
func.__code__следует заменить на
func.func_code
вот более четкое решение.
def freeze(o): if isinstance(o,dict): return frozenset({ k:freeze(v) for k,v in o.items()}.items()) if isinstance(o,list): return tuple([freeze(v) for v in o]) return o def make_hash(o): """ makes a hash out of anything that contains only list,dict and hashable types including string and numeric types """ return hash(freeze(o))
обновлено с 2013 года ответ...
ни один из приведенных выше ответов не кажется мне надежным. Причина-использование элементов (). Насколько я знаю, это происходит в машинно-зависимом порядке.
- Как насчет этого?
import hashlib def dict_hash(the_dict, *ignore): if ignore: # Sometimes you don't care about some items interesting = the_dict.copy() for item in ignore: if item in interesting: interesting.pop(item) the_dict = interesting result = hashlib.sha1( '%s' % sorted(the_dict.items()) ).hexdigest() return result
чтобы сохранить порядок ключей, а не
hash(str(dictionary))илиhash(json.dumps(dictionary))Я бы предпочел быстрое и грязное решение:from pprint import pformat h = hash(pformat(dictionary))Он будет работать даже для таких типов, как
DateTimeи многое другое, что не является сериализуемым JSON.
приведенный ниже код избегает использования функции Python hash (), потому что он не будет предоставлять хэши, которые согласованы при перезапусках Python (см. хэш-функция в Python 3.3 возвращает разные результаты между сеансами).
make_hashable()преобразовать объект во вложенные кортежи иmake_hash_sha256()также преобразованияrepr()к хэшу SHA256 в кодировке base64.import hashlib import base64 def make_hash_sha256(o): hasher = hashlib.sha256() hasher.update(repr(make_hashable(o)).encode()) return base64.b64encode(hasher.digest()).decode() def make_hashable(o): if isinstance(o, (tuple, list)): return tuple((make_hashable(e) for e in o)) if isinstance(o, dict): return tuple(sorted((k,make_hashable(v)) for k,v in o.items())) if isinstance(o, (set, frozenset)): return tuple(sorted(make_hashable(e) for e in o)) return o o = dict(x=1,b=2,c=[3,4,5],d={6,7}) print(make_hashable(o)) # (('b', 2), ('c', (3, 4, 5)), ('d', (6, 7)), ('x', 1)) print(make_hash_sha256(o)) # fyt/gK6D24H9Ugexw+g3lbqnKZ0JAcgtNW+rXIDeU2Y=
общий подход в порядке, но вы можете рассмотреть метод хеширования.
SHA был разработан для криптографической прочности (скорость тоже, но сила важнее). Вы можете принять это во внимание. Поэтому, используя встроенный
hashфункция, вероятно, хорошая идея, если безопасность не является каким-то ключом здесь.
Comments