谁能解释这个奇怪的错误迭代一套?

时间:2017-08-03 16:20:16

标签: python python-2.7 set iteration

我有一个for thing in a_set:形式的循环。它工作不正确,因为偶尔和不一致,它会从集合中拉两次相同的东西。 (这不会导致程序崩溃。它只是得到了错误的答案。)我无法确定任何对错误行为具有确定性的东西;但是我调试它的尝试很明显,有时会发生奇怪的事情。在我最密切观察的情况下,集合中有3个项目(之前和之后),循环执行4次,一次重复其中一个项目。这些项是对我创建的类的对象的引用(更像是一个C结构)。当我将for语句更改为for thing in list(a_set):时,不良行为就消失了。

我完全无法解释错误的行为。我非常肯定循环体中没有任何东西可以导致它发生的事情发生两次或改变事物变量的值。我相当肯定循环中发生的事情不会试图影响集合的组成。此外,即使可以,我认为这会导致RuntimeError。我对提出可能造成这种情况的假设提出了完全的损失。缺乏连续运行相同代码的可重复性尤其神秘。我尝试在更简单的场景中重新创建症状失败了。尽管如此,我仍然觉得将list()调用留在那里只是为了解决一个我无法解释的问题。任何其他人的假设都会受到欢迎。我需要了解在调试时我应该尝试消除哪些类型的想法。

更新:我认为这个问题被错误地搁置了,因为声称它不在主题上。在这种情况下,缺乏可重复性是一个问题,我怀疑我缺少这种语言的一些细微差别。事实上,情况确实如此,MSeifert的回答让我了解了导致它的原因。然而,正如我在回答他的回答时所指出的那样,这并不像他推测的那么简单。

我也对这个问题感到困惑,说集合中的对象是可变的。他们不是。它们是对属性可更改的对象的引用。 (这可以从我写的内容中推断出来,但我错误地使用了" mutable"在一般意义上而不是Python技术意义上。)什么是散列是对象的地址,独立其属性的值。如果这些对象引用是可变的,那么Python绝不会让我把它们放在一个集合中。

1 个答案:

答案 0 :(得分:6)

如果在添加list(a_set)时错误消失,则很可能在迭代期间更改了设置。一般情况下,这会抛出一个RuntimeError但是如果您添加了多少元素,则不会触发:

a = {1,2,3}
for item in a:
    print(item)
    a.add(item+3)  # add one item
    a.remove(item) # remove one item

将数字1打印到31(数量实际上是一个实现细节,因此您可能会看到不同的数量)以及循环之前和之后以及每次迭代开始时{{ 1}}包含set个元素。

但是,如果我添加3调用,它会创建原始集的副本(作为列表),并且只迭代原始集中存在的元素:

list

打印:

a = {1,2,3}
for item in list(a):
    print(item)
    a.add(item+3)
    a.remove(item)

print(a)

在评论中你注意到你在集合中的类是可变的,所以即使你可能你删除并添加相同的元素它可能不再是相同的元素(来自1 2 3 set([4, 5, 6]) # totally changed! )的观点。通常,您不应将可变类放在set中或作为set中的键,因为您必须非常小心,可变性不会影响dict或{{的结果1}}方法。

只是一个迭代看似“随机”数量的集合元素的例子:

__hash__

实际上会显示“随机”(不是真正随机的,它只取决于实例的哈希值,在这种情况下,哈希值取决于每次运行代码时类对象的__eq__)每次运行代码段时class Fun(object): def __init__(self, value): self.value = value def __repr__(self): return '{self.__class__.__name__}({self.value})'.format(self=self) def __eq__(self, other): return self.value == other.value a = {Fun(1),Fun(2),Fun(3)} for item in a: print(item) a.add(Fun(item.value+3)) a.remove(item) 个对象的数量。