重新插入集合元素会产生洞

Reinserting set element creates holes

一般来说,如果我们需要将一个对象插入到一个集合中,我们应该使其可哈希(通过实现哈希函数)和可比较(并实现比较函数)。 Set 不提供访问其元素的机制,因此不能直接改变,但可以很容易地绕过。 改变集合项目的一般模式如下

i = next(iter(x))
update(i)
x.add(i)

这似乎适用于几乎所有情况,但出现意外漏洞时除外。

class Foo(object):
    def __init__(self, x):
        self.x = x
        self.count = 0
    def __hash__(self):
        return hash((self.x, ))
    def __iadd__(self, n):
        self.count += n
    def __eq__(self, other):
        return self.x == other.x


>>> x = {Foo(1)}
>>> i = next(iter(x))
>>> i+=1
>>> x.add(i)
>>> x
set([None, <__main__.Foo object at 0x0279D8B0>])

我的猜测是在更新时改变一个 set 元素可能会导致意外行为,但调用 next 只会获取应该不是问题的值(我猜是一个副本)。

知道问题出在哪里吗?

Per the docs

[__iadd__] should attempt to do the operation in-place (modifying self) and return the result (which could be, but does not have to be, self)

因此,

def __iadd__(self, n):
    self.count += n
    return self

然后,

class Foo(object):
    def __init__(self, x):
        self.x = x
        self.count = 0
    def __hash__(self):
        return hash((self.x, ))
    def __iadd__(self, n):
        self.count += n
        return self
    def __eq__(self, other):
        return self.x == other.x

x = {Foo(1)}
i = next(iter(x))
i+=1
x.add(i)
print(x)

产量

set([<__main__.Foo object at 0x7f19ae8b9f10>])

您可能想 return 在 iadd 方法中自我。