【问题标题】:Computing an md5 hash of a data structure计算数据结构的 md5 哈希
【发布时间】:2011-07-22 00:52:28
【问题描述】:

我想计算一个 md5 散列,而不是字符串,而是整个数据结构。我了解执行此操作的方法的机制(发送值的类型、规范化字典键顺序和其他随机性、递归到子值等)。但它似乎是一种通常有用的操作,所以我很惊讶我需要自己滚动这个。

在 Python 中有没有更简单的方法来实现这一点?

更新:建议使用 pickle,这是一个好主意,但 pickle 不会规范化字典键顺序:

>>> 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

【问题讨论】:

  • 我觉得这个建议有点脏,但是你能md5sum你的数据结构的pickled版本吗?
  • 酸洗没有什么脏东西,只是不能满足哈希的需要。
  • 哎呀,真可惜。我希望它可以为您节省大量的精力。 :)

标签: python data-structures md5


【解决方案1】:

ROCKY 方式:将所有结构项放在一个父实体中(如果还没有的话),对它们进行递归和排序/规范化/等,然后计算其repr 的 md5。

【讨论】:

  • 我宁愿不改变数据结构来适应散列任务。
【解决方案2】:

更新:由于键顺序随机性,这不适用于字典。抱歉,我没有想到。

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

这应该适用于任何 python 数据结构,也适用于对象。

【讨论】:

  • 酸洗不能修复字典键的随机性。
【解决方案3】:

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

【讨论】:

  • 是的,bencode 似乎完全符合我的想象,但具有可逆的额外功能。
  • 需要注意的是,bencode 不是标准的 Python 2 或 3 模块。
  • 它也没有像pickle那样编码那么多的数据结构:-(
  • 我认为这不再是公认的解决方案。 bencode 依赖于一个似乎不再可用的模块 BTL(参见github.com/bittorrent/bencode/issues/3)。
  • @DavidKaufman 记者发现的问题是相对导入的工作方式发生了变化。模块 bencode 包含一个目录,其中包含一个从目录中的 BTL.py 文件导入的 __init__.py 文件。 BTL 模块就在 bencode 包中。只是现在这样的导入必须使用点前缀形式。但是有一些旧版本的 bencode 模块是一个独立的文件。
【解决方案4】:

我最终自己写了它,因为我认为我必须这样做:

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()

【讨论】:

  • 你将如何处理set 类型?我想我会说,与处理元组和列表的方式相同,不同之处在于它应该首先是sorted()。你同意吗?
  • 是的,sorted(v) 将是要走的路。
  • 当心,对update() 的调用本质上是连接的。所以这会将{'test': 1} 散列为与['test', 1]test1 相同的东西。这可能是也可能不是所需的行为。 docs.python.org/2/library/md5.html
  • @RossHemsley 你忽略了self.md5.update(str(type(v)))
  • 没错,这有帮助。可能值得注意的是,这样做有一些极端情况。
【解决方案5】:

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).encode('utf-8')).hexdigest()

print(data_md5)

打印:

87e83d90fc0d03f2c05631e2cd68ea02

【讨论】:

  • 非常好,python 中始终存在 hashlib 和 json
  • 不错的解决方案,但请记住,如果没有额外工作,某些数据类型无法转换为 JSON - datetime 是最常见的。 data = ['1234', 234, datetime.datetime(2013,1,1)] hashlib.md5(json.dumps(a, sort_keys=True)).hexdigest() 导致TypeError: datetime.datetime(2013, 1, 1, 0, 0) is not JSON serializable
  • @Boris:很容易让json 模块序列化更多数据类型(包括大多数用户定义类的实例以及datetime.datetime 实例),如my answer 所示问题Making object JSON serializable with regular encoder
  • 在python3中会有一个TypeError: Unicode-objects must be encoded before hashing所以用这个data_md5 = hashlib.md5(json.dumps(data, sort_keys=True).encode('utf-8')).hexdigest()
  • 纯 Python 中确定性哈希函数的非常聪明的解决方案
【解决方案6】:

虽然它确实需要对joblib 的依赖,但我发现joblib.hashing.hash(object) 工作得很好,并且设计用于joblib 的磁盘缓存机制。从经验上看,它似乎在每次运行中产生了一致的结果,即使在 pickle 在不同运行中混合的数据上也是如此。

另外,您可能对artemis-mlcompute_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
    

    【讨论】:

      猜你喜欢
      • 2010-12-07
      • 2012-04-14
      • 1970-01-01
      • 2011-12-08
      • 2017-01-27
      • 1970-01-01
      • 2011-08-27
      • 2015-12-18
      • 2019-10-03
      相关资源
      最近更新 更多