Python 有人能解释这个奇怪的错误在集合上迭代吗?

Python 有人能解释这个奇怪的错误在集合上迭代吗?,python,python-2.7,set,iteration,Python,Python 2.7,Set,Iteration,我有一个形式的循环,用于一个集合中的东西:。它工作不正常,因为它偶尔会不一致地从集合中拉出同一个东西两次。(这不会导致程序崩溃。它只是得到了错误的答案。)我无法确定关于错误行为的任何确定性;但我调试它的尝试清楚地表明,这种奇怪的情况有时会发生。在我最仔细观察的情况下,集合中有3项(之前和之后),循环执行4次,其中一项重复一次。这些项是对我创建的类(更像是C结构)的对象的引用。当我将for语句更改为for thing in list(a_set):时,坏行为消失了 我完全无法解释这种错误的行为。我

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

我完全无法解释这种错误的行为。我非常确定,循环体中的任何内容都不会导致它正在做的事情发生两次或更改thing变量的值。我相当肯定,循环中发生的事情不会试图影响集合的组成。此外,即使可以,我相信这也会导致运行时错误。我完全不知道是什么原因导致了这种情况。连续运行同一代码缺乏可重复性尤其令人费解。我试图在一个更简单的场景中重现症状的尝试失败了。然而,如果为了解决一个我无法解释的问题而将
list()
调用留在那里,我会觉得很傻。任何人的假设都是受欢迎的。我需要知道在调试过程中应该消除哪些东西

更新:我认为这个问题被错误地搁置了,因为有人声称它离题了。在这种情况下,缺乏再现性是问题所在,我怀疑我所缺少的语言有一些细微差别。事实证明确实如此,姆塞弗特的回答让我明白了是什么导致了这一现象。然而,正如我在对他的回答的评论中所指出的,这并不像他推测的那么简单


我还说集合中的对象是可变的,从而混淆了这个问题。事实并非如此。它们是对属性可更改的对象的引用。(这可以从我所写的内容中推断出来,但我在一般意义上错误地使用了“可变”一词,而不是在Python技术意义上。)散列的是对象的地址,与它的属性值无关。如果这些对象引用是可变的,Python一开始就不会让我将它们放在一个集合中。

如果在添加
列表(a_set)
时错误消失,则很可能是在迭代过程中更改了集合。通常,这会引发一个
运行时错误
,但如果添加的元素和删除的元素一样多,则不会触发:

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
(金额实际上是一个实现细节,因此您可以看到不同的金额),循环前后以及每次迭代开始时
集合包含
3
元素

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

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!

在注释中,您注意到集合中的类是可变的,因此即使您认为删除并添加了相同的元素,它也可能不再是相同的元素(从
集合的角度来看)。一般来说,您不应该将可变类放在
集合中或作为
dict
中的键,因为您必须非常小心,以确保可变性不会影响
\uuuuuuuuuuuuuuu散列
\uuuuuuuuuuueq
方法的结果

仅举一个迭代看似“随机”数目的集合元素的示例:

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)

将实际显示一个“随机”(不是真正的随机,它只取决于实例的哈希值,在这种情况下,哈希值取决于类对象的
id
,每次运行代码时会发生更改)每次运行代码段时
Fun
对象的数量。

如果在添加
列表(a\u集)时错误消失了
您很可能在迭代过程中更改了集合。通常,这会引发一个
运行时错误
,但如果添加的元素和删除的元素一样多,则不会触发:

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
(金额实际上是一个实现细节,因此您可以看到不同的金额),循环前后以及每次迭代开始时
集合包含
3
元素

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

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!

在注释中,您注意到集合中的类是可变的,因此即使您认为删除并添加了相同的元素,它也可能不再是相同的元素(从
集合的角度来看)。一般来说,您不应该将可变类放在
集合中或作为
dict
中的键,因为您必须非常小心,以确保可变性不会影响
\uuuuuuuuuuuuuuu散列
\uuuuuuuuuuueq
方法的结果

仅举一个迭代看似“随机”数目的集合元素的示例:

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)

将实际显示一个“随机”(不是真正的随机,它只取决于实例的散列,在这种情况下,散列取决于类对象的
id
,每次运行代码时都会发生更改)每次运行代码段时
Fun
对象的数量。

您需要某种方法来重现此情况。您的集合中是否有可更改的对象