当Set中的对象被更改为彼此匹配时会发生什么?

时间:2013-11-13 12:04:48

标签: python hash set

正如标题所示,我有一个关于更改集合中的对象的问题,使它们变得完全相同(在集合的眼睛中)。好奇。

关于Python,我问这个问题,但如果它是可推广的,请随意这样做。

如果我在Python中正确理解,Set iterable将通过等同它们的哈希来确定对象是否“相等”。因此对于 a b 对象,这将是:

hash(a) == hash(b)

对于您制作的任何对象,您可以根据自己的喜好覆盖standard hash function__hash__

假设您创建了一个哈希函数,它接受对象中的几个或所有对象,并使用哈希的组合作为自己的对象(例如,通过对它们进行OR运算)。

现在,如果你在一个Set中有几个最初不同的对象,并因此遍历该Set并改变其内部对象匹配的对象,那么Set会发生什么?他们都会留在那里,或者他们会被踢出去,还是我们需要等到对该套装进行操作?或者我们是否在某处提出了一些错误?

4 个答案:

答案 0 :(得分:6)

考虑这个测试:

class A:
    def __init__(self, h):
        self.h = h

    def __hash__(self):
        return self.h

x = A(1)
y = A(2)

a = {x, y}

print x in a, y in a
print a

print "----"

x.h = 2

print x in a, y in a
print a

结果:

True True
set([<__main__.A instance at 0x10d94fd40>, <__main__.A instance at 0x10d94fd88>])
----
False True
set([<__main__.A instance at 0x10d94fd40>, <__main__.A instance at 0x10d94fd88>])

如您所见,第一个对象x仍然存在,但in运算符报告它不存在。为什么会这样?

根据我的理解,Set对象是使用哈希表实现的,哈希表通常有这样的结构:

 hash_value => list of objects with this hash value
 another_hash_value => list of objects with this hash value

当一个Set回答in个请求时,它首先计算参数的哈希值,然后尝试在相应的列表中找到它。我们的a集最初是这样的:

  1 => [x]
  2 => [y]

现在,我们更改x的哈希并询问该对象是否在那里。该集计算哈希值(现在为2)尝试在第二个列表中找到x并失败 - 因此False

为了让事情变得更有趣,我们来做吧

a.add(x)
print x in a, y in a
print a

结果:

True True
set([<__main__.A instance at 0x107cbfd40>, 
     <__main__.A instance at 0x107cbfd88>, 
     <__main__.A instance at 0x107cbfd40>])

现在我们在集合中有两次相同的对象!如您所见,没有自动调整也没有错误。 Python是一种成熟的语言,总是假设你知道自己在做什么。

答案 1 :(得分:5)

不允许以更改其哈希值的方式修改集合的成员。

在Python中,您只能在集合中存储 hashable 对象。来自documentation(强调我的):

  

如果它的哈希值在其生命周期内永远不会改变(它需要__hash__()方法),并且可以与其他对象进行比较(它需要一个{ {1}}或__eq__()方法)。 比较相等的Hashable对象必须具有相同的哈希值。

     

Hashability使对象可用作字典键和set成员,因为这些数据结构在内部使用哈希值。

     

所有Python的不可变内置对象都是可清除的,而没有可变容器(例如列表或字典)。默认情况下,作为用户定义类实例的对象是可清除的;他们都比较不等(除了他们自己),他们的哈希值是他们的__cmp__()

如果你违反了这份合同(正如你在提问中提出的那样),该套装无法完成任务并且所有赌注都已关闭。

修改集合成员的正确方法是删除,更改和重新添加。这将按照您的预期行事。

  

[set]将通过等同它们的哈希来确定对象是否“相等”

这不太正确。比较散列不能用于确定对象相等。它只能用于确定对象不等。这是一个微妙但重要的区别。

答案 2 :(得分:2)

首先,set的元素必须是hashable

  

集合的元素必须是可散列的。

虽然hashable表示:

  

如果一个对象具有一个在其生命周期内永远不会改变的哈希值[...]

,则该对象是可清除的

因此,只要您不以其哈希值(其__hash__方法的结果)保持不变的方式更改对象,一切都会正常。

在Python中常见的是,不可变对象被认为是可散列的,而可变对象则不是:

  

所有Python的不可变内置对象都是可清除的,而没有可变容器(例如列表或字典)。

答案 3 :(得分:0)

一起哈希哈希会产生一个特别糟糕的哈希函数,因为你会越来越倾向于设置更多位的值。集合和字典仍然使用散列表的散列表;预期会发生冲突,并对具有相同哈希值的对象执行更深入的比较。但是,如果哈希函数不好,你会失去哈希表的优势--O(1)查找。

同样如其他答案所述,集合应该只保存不可变的值。将对象插入集合后更改对象的哈希值会破坏集合类型的条件,并且检查对象是否在集合中,甚至从集合中删除对象等操作都将失败。不过,我希望你仍然可以通过迭代来找到它。