在字典中使用不可哈希的Python对象作为键

4 投票
8 回答
6635 浏览
提问于 2025-04-15 15:18

在Python中,不能把不可哈希的对象用作字典的键。正如Andrey Vlasovskikh所提到的,对于使用非嵌套字典作为键的特殊情况,有一个不错的解决办法:

frozenset(a.items())#Can be put in the dictionary instead

有没有办法使用任意对象作为字典的键呢?

举个例子:

这个怎么能作为键呢?

{"a":1, "b":{"c":10}}

实际上,你在代码中遇到这种情况的可能性非常小。如果你觉得有必要这样做,建议先考虑调整一下你的数据模型。

具体使用场景

这个使用场景是为了缓存对某个只接受关键字参数的函数的调用。字典中的每个键都是一个字符串(参数的名字),而对象可能会非常复杂,包含多层字典、列表、元组等等。

相关问题

这个子问题是从这里的问题分出来的。这里的解决方案主要处理的是非嵌套字典的情况。

8 个回答

6

别这样。我同意Andreys在之前问题中的评论,使用字典作为键是没有意义的,尤其是嵌套的字典。你的数据模型显然很复杂,字典可能不是合适的选择。你应该尝试一些面向对象的编程方法。

7

这段内容是基于Chris Lutz的解决方案。

import collections

def hashable(obj):
    if isinstance(obj, collections.Hashable):
        items = obj
    elif isinstance(obj, collections.Mapping):
        items = frozenset((k, hashable(v)) for k, v in obj.iteritems())
    elif isinstance(obj, collections.Iterable):
        items = tuple(hashable(item) for item in obj)
    else:
        raise TypeError(type(obj))

    return items
4

这个内容是基于Chris Lutz的解决方案。请注意,这个方法不适用于在遍历过程中会改变的对象,比如流(streams),也不适用于有循环的情况。

import collections

def make_hashable(obj):
    """WARNING: This function only works on a limited subset of objects
    Make a range of objects hashable. 
    Accepts embedded dictionaries, lists or tuples (including namedtuples)"""
    if isinstance(obj, collections.Hashable):
        #Fine to be hashed without any changes
        return obj
    elif isinstance(obj, collections.Mapping):
        #Convert into a frozenset instead
        items=list(obj.items())
        for i, item in enumerate(items):
                items[i]=make_hashable(item)
        return frozenset(items)
    elif isinstance(obj, collections.Iterable):
        #Convert into a tuple instead
        ret=[type(obj)]
        for i, item in enumerate(obj):
                ret.append(make_hashable(item))
        return tuple(ret)
    #Use the id of the object
    return id(obj)

撰写回答