在Python中对元组进行哈希,考虑顺序的影响?

6

我有:

tuple1 = token1, token2
tuple2 = token2, token1
for tuple in [tuple1, tuple2]:
    if tuple in dict:
        dict[tuple] += 1
    else:
        dict[tuple] = 1

然而,元组1和元组2都获得了相同的计数。有什么方法可以哈希一组两个事物,使其顺序有所区别?

3个回答

23

在哈希时考虑顺序:

>>> hash((1,2))
1299869600
>>> hash((2,1))
1499606158

这假设对象本身具有唯一的哈希值。即使它们没有,当在字典中使用时仍然可以正常运行(只要对象本身不相等,根据其__eq__方法定义):
>>> t1 = 'a',hash('a') 
>>> [hash(x) for x in t1]  #both elements in the tuple have same hash value since `int` hash to themselves in cpython
[-468864544, -468864544]
>>> t2 = hash('a'),'a'
>>> hash(t1)
1486610051
>>> hash(t2)
1486610051
>>> d = {t1:1,t2:2}  #This is OK.  dict's don't fail when there is a hash collision
>>> d
{('a', -468864544): 1, (-468864544, 'a'): 2}
>>> d[t1]+=7
>>> d[t1]
8
>>> d[t1]+=7
>>> d[t1]
15
>>> d[t2]   #didn't touch d[t2] as expected.
2

请注意,由于哈希碰撞,这个字典可能比另一个没有哈希碰撞的字典效率低。 :)

假设token1token2本身的hash()值不同。 - Silas Ray
@sr2222 -- 当然可以,但我认为值得记录的是,当你有一个包含该类实例的元组时,你可以以这样的方式覆盖类中的__hash__使得顺序无关紧要...但即使如此,根据__eq__的实现方式,你的dict仍然可能正常运行(尽管由于哈希冲突而非常低效)。 - mgilson
@sr2222 -- 我根据你的评论进一步更新了我的答案。这对我来说很有趣,我想你也可能会觉得有趣。 - mgilson
很有趣。我需要重新阅读关于字典内部工作原理的文档... - Silas Ray
1
@sr2222 -- 我在观看我转发的这里视频时学到了很多东西。 - mgilson

7
他们得到相同的计数是因为你的代码明确同时增加了token1,token2token2,token1的计数。如果你不这样做,计数就不会保持同步:
In [16]: import collections

In [17]: d = collections.defaultdict(int)

In [18]: d[1,2] += 1

In [19]: d[1,2]
Out[19]: 1

In [20]: d[2,1]
Out[20]: 0

1
我认为通常会将其编写为d[(1,2)]而不是d[1,2],尽管它们是等价的... - mgilson

0

看起来你已经发布了一个循环体的实例。我可以建议你使用 collections.Counter 来完成你想要做的事情,它可以在一行代码中实现你想要的功能:

counter = (collections.Counter(myListOfTuples) + 
           collections.Counter([j,i for i,j in myListOfTuples]))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接