Python不允许将不可清除的对象用作其他字典中的键。正如Andrey Vlasovskikh指出的那样,对于使用非嵌套字典作为键的特殊情况,有一个很好的解决方法:
frozenset(a.items())#Can be put in the dictionary instead
是否有使用任意对象作为词典中的键的方法?
示例:
如何将其用作密钥?
{"a":1, "b":{"c":10}}
您实际上必须在代码中使用类似的东西是非常罕见的。如果您认为是这种情况,请考虑先更改数据模型。
确切的用例
用例是缓存对任意关键字唯一函数的调用。字典中的每个键都是一个字符串(参数的名称),对象可能非常复杂,包括分层的字典,列表,元组等。
相关问题
此子问题已与the problem here分开。这里的解决方案处理字典没有分层的情况。
答案 0 :(得分:7)
别。我同意Andreys对前一个问题的评论,即将字典作为键,而不是嵌套字典是没有意义的。您的数据模型显然非常复杂,字典可能不是正确的答案。你应该尝试一些OO。
答案 1 :(得分: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
答案 2 :(得分:3)
基于Chris Lutz的解决方案。请注意,这不会处理通过迭代更改的对象(如流),也不处理循环。
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)
答案 3 :(得分:2)
如果你真的必须,可以使你的物品可以清洗。将您想要放入的任何内容作为键子类,并提供__hash__函数,该函数返回此对象的唯一键。
举例说明:
>>> ("a",).__hash__()
986073539
>>> {'a': 'b'}.__hash__()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not callable
如果您的哈希值不够独特,则会发生冲突。也可能很慢。
答案 4 :(得分:2)
我完全不同意评论&amp;回答说不应该为数据模型纯度原因做这个。
字典将对象与另一个对象相关联,使用前一个对象作为关键字。字典不能用作键,因为它们不可清除。将字典映射到其他对象并没有那么有意义/实际/必要。
据我了解Python绑定系统,您可以将任何字典绑定到许多变量(或者相反,取决于您的术语),这意味着这些变量都知道与该字典相同的唯一“指针”。是不是可以将该标识符用作散列密钥? 如果您的数据模型确保/强制您不能拥有两个使用相同内容作为键的字典,那对我来说这似乎是一种安全的技术。
我应该补充一点,我不知道如何做/应该怎么做。
我不完全是这应该是答案还是评论。如果需要请纠正我。
答案 5 :(得分:1)
使用recursion!
def make_hashable(h):
items = h.items()
for item in items:
if type(items) == dict:
item = make_hashable(item)
return frozenset(items)
您可以为要进行哈希处理的任何其他可变类型添加其他类型测试。这应该不难。
答案 6 :(得分:1)
使用装饰器时遇到此问题,该装饰器根据调用签名缓存先前调用的结果。我不同意这里对“你不应该这样做”的评论/答案,但我认为重要的是要认识到沿着这条道路走下来时可能会出现意外和意外的行为。我的想法是,由于实例都是可变的和可清除的,并且改变它似乎不切实际,因此创建不可清除的类型或对象的可混合等效物并没有任何本质上的错误。但当然这只是我的意见。
对于需要兼容Python 2.5的任何人,以下内容可能会有用。我基于早先的答案。
from itertools import imap
tuplemap = lambda f, data: tuple(imap(f, data))
def make_hashable(obj):
u"Returns a deep, non-destructive conversion of given object to an equivalent hashable object"
if isinstance(obj, list):
return tuplemap(make_hashable, iter(obj))
elif isinstance(obj, dict):
return frozenset(tuplemap(make_hashable, obj.iteritems()))
elif hasattr(obj, '__hash__') and callable(obj.__hash__):
try:
obj.__hash__()
except:
if hasattr(obj, '__iter__') and callable(obj.__iter__):
return tuplemap(make_hashable, iter(obj))
else:
raise NotImplementedError, 'object of type %s cannot be made hashable' % (type(obj),)
else:
return obj
elif hasattr(obj, '__iter__') and callable(obj.__iter__):
return tuplemap(make_hashable, iter(obj))
else:
raise NotImplementedError, 'object of type %s cannot be made hashable' % (type, obj)
答案 7 :(得分:1)
我同意Lennart Regebro你不同意。但是我经常发现缓存一些函数调用,可调用对象和/或Flyweight对象很有用,因为它们可能使用关键字参数。
但如果你真的想要它,试试pickle.dumps
(或cPickle
如果python 2.6)作为一个快速而肮脏的黑客。它比使用递归调用使项目不可变的任何答案快得多,并且字符串是可清除的。
import pickle
hashable_str = pickle.dumps(unhashable_object)