在Python中对不可变字典进行散列

在Python中对不可变字典进行散列,python,dictionary,hash,immutability,python-3.2,Python,Dictionary,Hash,Immutability,Python 3.2,简短版本:实现为无序项字典的多集的最佳哈希算法是什么 我正在尝试散列一个作为字典实现的不可变多集(在其他语言中是一个包或多集:类似于一个数学集,只是它可以容纳每个元素中的多个)。我已经创建了标准库类collections.Counter的一个子类,类似于此处的建议:,建议使用如下哈希函数: 类冻结计数器(collections.Counter): # ... 定义散列(自我): 返回哈希(元组(已排序(self.items())) 创建项目的完整元组会占用大量内存(比如说,相对于使用生成器),

简短版本:实现为无序项字典的多集的最佳哈希算法是什么

我正在尝试散列一个作为字典实现的不可变多集(在其他语言中是一个包或多集:类似于一个数学集,只是它可以容纳每个元素中的多个)。我已经创建了标准库类
collections.Counter
的一个子类,类似于此处的建议:,建议使用如下哈希函数:

类冻结计数器(collections.Counter):
# ...
定义散列(自我):
返回哈希(元组(已排序(self.items()))
创建项目的完整元组会占用大量内存(比如说,相对于使用生成器),而散列将发生在我的应用程序中内存非常密集的部分。更重要的是,我的字典键(多集元素)可能无法排序

我正在考虑使用这个算法:

def\uuuu散列(self):
return functools.reduce(lambda,b:a^b,self.items(),0)
我认为使用按位异或意味着与元组哈希不同的是,哈希值的顺序并不重要。我想我可以在数据的无序元组流上半实现Python元组哈希算法。请参阅(在页面中搜索单词'hash')——但我几乎不知道足够的C来阅读它

思想?建议?谢谢。
(如果你想知道为什么我在尝试散列一个多集:我的问题的输入数据是多集集的集合,在每一个多集集合中,每个多集都必须是唯一的。我在一个截止日期前工作,我不是一个有经验的编码员,所以我希望尽可能避免发明新的算法。这似乎是最具python风格的wa要确保我有一堆唯一的东西,就是把它们放在一个
集合()
,但这些东西必须是可散列的。)

我从评论中收集到的 @marcin和@senderle给出了几乎相同的答案:使用
hash(frozenset(self.items())
。这是有道理的,因为@marcin是第一个,但我选中了@senderle,因为我对不同解决方案的big-O运行时间进行了很好的研究@marcin也提醒我——但是从
dict
继承的那一个会很好地工作。这就是我实现一切的方式——欢迎基于此代码的进一步评论和建议:

类冻结计数器(collections.Counter):
#编辑:此代码的早期版本包含一个__插槽__)定义。
#但是,从Python文档中可以看出:“当从没有
#_uuuslots_uuuuu,该类的uuu dict_uuuu属性将始终可访问,
#因此,子类中的____;定义毫无意义。”
# http://docs.python.org/py3k/reference/datamodel.html#notes-关于插槽的使用
# ...
定义散列(自我):
“实现哈希(self)->int”
如果不是hasattr(self,“\u hash”):
self.\u hash=hash(frozenset(self.items()))
返回self.\u散列

由于字典是不可变的,因此可以在创建字典时创建哈希并直接返回。我的建议是创建一个from
items
(在2.7中的3+;
iteritems
),对其进行散列,并存储散列

要提供一个明确的示例:

>>>> frozenset(Counter([1, 1, 1, 2, 3, 3, 4]).iteritems())
frozenset([(3, 2), (1, 3), (4, 1), (2, 1)])
>>>> hash(frozenset(Counter([1, 1, 1, 2, 3, 3, 4]).iteritems()))
-3071743570178645657
>>>> hash(frozenset(Counter([1, 1, 1, 2, 3, 4]).iteritems()))
-6559486438209652990
为了阐明为什么我更喜欢
frozenset
而不是一组排序的项:一个
frozenset
不必对项进行排序,因此初始哈希在O(n)时间而不是O(n log n)时间内完成。这可以从和实现中看出


另请参见Raymond Hettinger描述的
frozenset
hash函数的实现。在这里,他明确解释了哈希函数如何避免对值进行排序以获得稳定的、顺序不敏感的值。

您是否考虑过
哈希(self.items()中x的排序(哈希(x))
?这样,您只需对整数进行排序,而不必构建列表

您也可以将元素散列异或在一起,但坦率地说,我不知道这会有多好(会有很多冲突吗?)。说到碰撞,难道你不需要实现
\uuuuuu eq\uuuu
方法吗


或者,与我的回答类似,
hash(frozenset(self.items())

任何可散列的对象都是可排序的。如果它是可散列的,那么它总是生成相同的散列,因此对散列进行排序。您确定要使
元组
占用大量内存吗?它只是指向dict中每个项目的一个“指针”,而不是它的副本。检查+1以获得对该问题的非常全面的更新,并提供一个最终实现。因此,您是说
hash(frozenset(myfrozendict.iteritems())
hash(myfrozendict.iteritems())
快,对于小而大的
myfrozendict
s?@MarcoSulla不,我是说它应该比
hash(tuple(sorted(self.items())))
更快(对于大对象)。我不相信
散列(myfrozendict.iteritems())
给出了有效的散列:不能保证两个相同的
myfrozendict
s的散列是相同的。这是因为
iteritems
的迭代顺序可能取决于插入顺序等因素。(因此需要排序。)好的。。。但我有一些疑问。你知道我在哪里可以找到
dict
hash()
实现吗?@MarcoSulla我不确定我是否理解。Dicts是不可散列的!对不起,我是说
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu