Подтвердить что ты не робот

Вычисление хеша md5 структуры данных

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

Есть ли более простой способ в Python для этого?

ОБНОВЛЕНИЕ: предложено рассол, и это хорошая идея, но травление не канонизирует порядок словаря:

>>> import cPickle as pickle
>>> import hashlib, random 
>>> for i in range(10):
...  k = [i*i for i in range(1000)]
...  random.shuffle(k)
...  d = dict.fromkeys(k, 1)
...  p = pickle.dumps(d)
...  print hashlib.md5(p).hexdigest()
...
51b5855799f6d574c722ef9e50c2622b
43d6b52b885f4ecb4b4be7ecdcfbb04e
e7be0e6d923fe1b30c6fbd5dcd3c20b9
aebb2298be19908e523e86a3f3712207
7db3fe10dcdb70652f845b02b6557061
43945441efe82483ba65fda471d79254
8e4196468769333d170b6bb179b4aee0
951446fa44dba9a1a26e7df9083dcadf
06b09465917d3881707a4909f67451ae
386e3f08a3c1156edd1bd0f3862df481
4b9b3361

Ответ 1

Словари bencode сортирует так:

import hashlib
import bencode
data = ['only', 'lists', [1,2,3], 
'dictionaries', {'a':0,'b':1}, 'numbers', 47, 'strings']
data_md5 = hashlib.md5(bencode.bencode(data)).hexdigest()
print data_md5

печатает:

af1b88ca9fd8a3e828b40ed1b9a2cb20

Ответ 2

json.dumps() может сортировать словари по ключу. Поэтому вам не нужны другие зависимости:

import hashlib
import json

data = ['only', 'lists', [1,2,3], 'dictionaries', {'a':0,'b':1}, 'numbers', 47, 'strings']
data_md5 = hashlib.md5(json.dumps(data, sort_keys=True)).hexdigest()

print(data_md5)

Печать

87e83d90fc0d03f2c05631e2cd68ea02

Ответ 3

Я закончил тем, что писал сам, так как думал, что мне придется:

class Hasher(object):
    """Hashes Python data into md5."""
    def __init__(self):
        self.md5 = md5()

    def update(self, v):
        """Add `v` to the hash, recursively if needed."""
        self.md5.update(str(type(v)))
        if isinstance(v, basestring):
            self.md5.update(v)
        elif isinstance(v, (int, long, float)):
            self.update(str(v))
        elif isinstance(v, (tuple, list)):
            for e in v:
                self.update(e)
        elif isinstance(v, dict):
            keys = v.keys()
            for k in sorted(keys):
                self.update(k)
                self.update(v[k])
        else:
            for k in dir(v):
                if k.startswith('__'):
                    continue
                a = getattr(v, k)
                if inspect.isroutine(a):
                    continue
                self.update(k)
                self.update(a)

    def digest(self):
        """Retrieve the digest of the hash."""
        return self.md5.digest()

Ответ 4

UPDATE: это не будет работать для словарей из-за случайности случайности. Извините, я об этом не думал.

import hashlib
import cPickle as pickle
data = ['anything', 'you', 'want']
data_pickle = pickle.dumps(data)
data_md5 = hashlib.md5(data_pickle).hexdigest()

Это должно работать для любой структуры данных python, а также для объектов.

Ответ 5

ROCKY way: Поместите все ваши элементы структуры в один родительский объект (если он еще не), recurse и sort/canonicalize/etc, а затем вычислите md5 его repr.

Ответ 6

Хотя для этого требуется зависимость от joblib, я обнаружил, что joblib.hashing.hash(object) работает очень хорошо и предназначен для использования с механизмом кэширования диска joblib. Эмпирический это, кажется, последовательные результаты от запуска к запуску, даже на данных, pickle смесей на различных трассах.

В качестве альтернативы вам может быть интересна функция artemis-ml compute_fixed_hash, которая теоретически хэширует объекты таким образом, чтобы это было согласованно при compute_fixed_hash. Однако я не проверял это сам.

Извините за пост миллионы лет после первоначального вопроса 😅

Ответ 7

Вы можете использовать встроенный pprint, который будет охватывать несколько больше случаев, чем предлагаемое json.dumps(). Например, datetime -objects будет обработан правильно.

Ваш пример переписан для использования pprint вместо json:

>>> import hashlib, random, pprint
>>> for i in range(10):
...     k = [i*i for i in range(1000)]
...     random.shuffle(k)
...     d = dict.fromkeys(k, 1)
...     print hashlib.md5(pprint.pformat(d)).hexdigest()
... 
b4e5de6e1c4f3c6540e962fd5b1891db
b4e5de6e1c4f3c6540e962fd5b1891db
b4e5de6e1c4f3c6540e962fd5b1891db
b4e5de6e1c4f3c6540e962fd5b1891db
b4e5de6e1c4f3c6540e962fd5b1891db
b4e5de6e1c4f3c6540e962fd5b1891db
b4e5de6e1c4f3c6540e962fd5b1891db
b4e5de6e1c4f3c6540e962fd5b1891db
b4e5de6e1c4f3c6540e962fd5b1891db
b4e5de6e1c4f3c6540e962fd5b1891db