在字典中使用不可哈希的Python对象作为键
在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)