使用不可散列的Python对象作为字典中的键
Python不允许将不可散列的对象用作其他字典中的键。正如Andrey Vlasovskikh所指出的,对于使用非嵌套字典作为键的特殊情况,有一个很好的解决方法:使用不可散列的Python对象作为字典中的键,python,Python,Python不允许将不可散列的对象用作其他字典中的键。正如Andrey Vlasovskikh所指出的,对于使用非嵌套字典作为键的特殊情况,有一个很好的解决方法: frozenset(a.items())#Can be put in the dictionary instead 有没有一种在字典中使用任意对象作为键的方法 示例: 如何将其用作钥匙 {"a":1, "b":{"c":10}} 在代码中实际使用类似的内容是极为罕见的。如果你认为是这样的话,首先考虑改变你的数据模型。< /强>
frozenset(a.items())#Can be put in the dictionary instead
有没有一种在字典中使用任意对象作为键的方法
示例:
如何将其用作钥匙
{"a":1, "b":{"c":10}}
在代码中实际使用类似的内容是极为罕见的。如果你认为是这样的话,首先考虑改变你的数据模型。< /强>
准确的用例
用例是缓存对任意只包含关键字的函数的调用。字典中的每个键都是一个字符串(参数的名称),对象可能非常复杂,由分层字典、列表、元组等组成
相关问题
这个子问题已从中分离出来。这里的解决方案处理字典不分层的情况。使用
您可以为任何其他要使其可散列的可变类型添加其他类型测试。这应该不难。不要。我同意Andreys对上一个问题的评论,即将字典作为键是没有意义的,尤其是不能嵌套字典。您的数据模型显然相当复杂,字典可能不是正确的答案。你应该尝试一些面向对象的方法。如果你真的必须,让你的对象可以散列。将任何要作为键放入的子类,并提供一个_散列函数,该函数将唯一键返回给该对象 举例说明:
>>> ("a",).__hash__()
986073539
>>> {'a': 'b'}.__hash__()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not callable
>>>(“a”),散列
986073539
>>>{'a':'b'}.\uuuuuhash\uuuuuuuu()
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
TypeError:“非类型”对象不可调用
如果您的散列不够唯一,则会发生冲突。也可能很慢。基于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)
我完全不同意评论和回答说,出于数据模型纯度的原因,不应该这样做 字典将一个对象与另一个对象关联,并将前者作为键。字典不能用作键,因为它们不可散列。这不会降低将词典映射到其他对象的意义/实用性/必要性 根据我对Python绑定系统的理解,您可以将任何字典绑定到多个变量(或相反,取决于您的术语),这意味着这些变量都知道指向该字典的相同唯一“指针”。难道不能使用该标识符作为散列键吗? 如果您的数据模型确保/强制执行不能将两个具有相同内容的字典用作键,那么对我来说,这似乎是一种安全的技术 我要补充的是,我完全不知道该怎么做
我不完全确定这是回答还是评论。如果需要,请更正。我在使用基于调用签名缓存以前调用结果的装饰程序时遇到此问题。我不同意这里关于“你不应该这样做”的评论/回答,但我认为,在走这条路的时候,认识到意外和意外行为的可能性是很重要的。我的想法是,由于实例既是可变的又是可散列的,而且改变这一点似乎并不实际,因此创建非散列类型或对象的可散列等价物本质上没有什么错。当然,这只是我的观点 对于任何需要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)
再次基于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
我同意Lennart Regebro的观点,你没有。然而,我经常发现缓存一些函数调用、可调用对象和/或Flyweight对象很有用,因为它们可能使用关键字参数 但是如果您真的想要它,可以尝试
pickle.dumps
(或者cPickle
如果是Python2.6)作为一种快速而肮脏的黑客攻击。它比任何使用递归调用使项不可变、字符串可散列的答案都要快得多
import pickle
hashable_str = pickle.dumps(unhashable_object)
事实上,我认为这有点复杂是对的。需要处理元组、列表、集合的特殊代码。。。实际上可能需要付出巨大的努力来妥善解决这个问题。我还应该重新命名这个问题,我认为应该是type(item)==dictalSetting item to make_hashable(item)没有在列表中设置它这是一个丑陋的黑客行为,它会分散代码的含义。如果没有人回答这个问题,那么我确实计划自己实现它(参见Chris Lutz的解决方案),并将解决方案发布在这里。然而,请随便回答,实际上这个问题相当棘手。您几乎可以保证获得类型冲突,即一种类型的dict与另一种类型的dict不同:-(仅存储类的类型不起作用,因为包含的类型可能也有类型:--1:这不是“非常罕见”。这是不必要的。使用适当的
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu哈希
函数创建不应进入对话。只需定义一个适当的类并消除此问题。我正在添加我的用例:一个类似于内存memoize
的函数。标准memoize
函数的问题是与db对话的开销。如果在内存中使用memoize
,没有简单的方法可以清除r当超时不是静态的时,缓存。我不同意它应该是注释。在我看来,这是正确的答案。YMMV当然。+1:不要。如果你认为你需要它,那么你的“嵌套字典”不应该是嵌套字典--它应该是一个具有适当的\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
import pickle
hashable_str = pickle.dumps(unhashable_object)