【问题标题】:why dict objects are unhashable in python?为什么 dict 对象在 python 中是不可散列的?
【发布时间】:2010-12-29 18:25:27
【问题描述】:

我的意思是为什么我们不能把 dict 的键作为 dict?

这意味着我们不能将字典作为另一个字典的键...

【问题讨论】:

    标签: python dictionary


    【解决方案1】:

    简答:因为它们是可变容器

    如果一个字典被散列,它的散列会随着你改变它的内容而改变。

    【讨论】:

    • 我什至不想开始想象使用对象作为键的噩梦逻辑。
    • 实际上 Python 使散列对象变得容易,它为每个对象提供了一个可以散列的唯一且恒定的标识。
    • 是的,但默认情况下,用户定义对象的实例总是比较不相等。
    • 如果必须的话,可以对字典进行哈希表示——例如frozenset(D.items())(对于D 字典)。 frozenset 是一个可散列且不可变的集合——set 是不可散列的,原因与 dict 相同。
    • 这个答案与没有frozendict的理由之一相矛盾 - legacy.python.org/dev/peps/pep-0416 - 用户可以“按照惯例同意”不改变共享的dict,所以“没有很大的需要”担心这个问题。
    【解决方案2】:

    这很容易处理。在散列之前将字典包装在 freezeset 中。然后,当您需要使用它时,将其转换回字典。

    >>> unhashable = {'b': 'a', 'a': 'b'}
    >>> hashable = frozenset(unhashable.items())
    >>> unhashable = dict(hashable)
    >>> unhashable
    {'a': 'b', 'b': 'a'}
    

    请注意,字典键顺序无论如何都是未定义的,因此键顺序的更改无关紧要。

    【讨论】:

    • 冻结字典的哈希值不依赖于值,只依赖于键。因此,对于那些希望将字典用作另一个字典的键的人来说,如果这就是您要查找的内容,那么这是行不通的。
    • 如果任何项目包含另一个dict,它将失败
    【解决方案3】:

    正如其他人所说,dict的哈希值随着内容的变化而变化。

    但是,如果您确实需要使用 dicts 作为键,您可以将 dict 子类化以制作可散列的版本。

    >>> class hashabledict(dict):
    ...    def __hash__(self):
    ...        return id(self)
    ... 
    >>> hd = hashabledict()
    >>> d = dict()
    >>> d[hd] = "foo"
    >>> d
    {{}: 'foo'}
    
    >>> hd["hello"] = "world"
    >>> d
    {{'hello': 'world'}: 'foo'}
    

    这会将用于 dict 的哈希值替换为对象在内存中的地址。

    【讨论】:

    • 然后我可以替换普通的dict bye dict = hashabledict
    • 但这没用:如果我在{} 下存储了一个值,我无法用{} 查找它,因为两个空的hashabledicts 具有不同的id 和不同的哈希值。哈希函数的重要之处在于它必须为两个“相等”的值返回相同的哈希值。
    • @Ned-Doh!你说的对。真正需要的是一个frozendict,它的行为方式与frozenset 相同。您可以将 dict 子类化来定义一个,就像在 ASPN 上的这个秘诀中一样:code.activestate.com/recipes/414283
    • 如果您只是想区分不同的字典,它可能会很有用。但要小心;如果一个 dict 被垃圾回收,一个新实例化的 dict 可能驻留在内存中的同一位置,从而产生相同的id(),从而导致难以找到的错误。
    【解决方案4】:

    Python 中的可变容器类型都不是hashable,因为它们是可变的,因此它们的哈希值可以在其生命周期内发生变化。

    【讨论】:

    • 元组和字符串是容器,但可散列且不可变。
    • @Ignacio Vazquez-Abrams:字符串是序列,但不是容器。此外,包含不可散列项的元组也是不可散列的;尝试使用([1], {2:3}) 作为字典键。
    【解决方案5】:

    可能由于错误的原因,我多次遇到这个问题;我想引用完整的dict 作为某事的关键。我不需要它是可变的,但我确实想保留并轻松访问dict 的成员。

    我发现使 dict 不可变并快速用作键值的最简单方法是将其设为 JSON(或以您喜欢的替代方式序列化)。

    例如:

    >>> import json
    >>> d = {'hey':1, 'there':2}
    >>> d_key = json.dumps(d)
    >>> d_key
    '{"there": 2, "hey": 1}'
    >>> d2 = {d_key: 'crazytown'}
    >>> d2
    {'{"there": 2, "hey": 1}': 'crazytown'}
    

    它很容易操作,因为它只是一个字符串。而且,如果你想引用它的成员,它可以被反序列化成一个对象。

    【讨论】:

    • 如果字典以不同的顺序序列化,这将失败。在字典中,元素的顺序无关紧要,但是当您从中创建一个字符串并对其进行比较时,两个具有相同内容的字典可能不匹配。
    • @MartinMelka 对于默认排序字典的 Python 3.7 仍然如此吗?
    猜你喜欢
    • 1970-01-01
    • 2015-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-28
    • 1970-01-01
    相关资源
    最近更新 更多