首页 > 解决方案 > 迭代期间的运行时错误?

问题描述

列表和集合之间的区别之一是列表可以在迭代期间更改——我们可以在循环中附加到它等等。但是,如果我们尝试在 for 循环期间添加到集合,则会引发运行时错误. 但是,Python 如何检测到 set.add() 正在循环中使用,然后引发运行时错误?如果我尝试重新创建我的伪列表类并在我的类的附加函数中引发运行时错误,我是否只是重载 __iter__ 以防止任何附加?

例如:

a_set = {1,2,3,4}
a_list = [1,2,3,4]

for i in a_list:
     a_list.append(5)

这导致无限循环

for j in a_set:
    a_set.add(5)

这会导致运行时错误。

它们都有 __iter__ 函数,所以在我的伪列表类中,我应该如何重载 __iter__ 以便它会像集合一样引发运行时错误?

标签: pythoniteration

解决方案


这一切都在您的可迭代类返回的迭代器中。请注意,对于一个集合,它实际上是__next__引发错误的方法,并且不一定在 for 循环中,尽管 for 循环隐式调用__iter__一个可迭代对象,然后调用__next__生成的迭代器并将其分配给循环变量,并且在每次迭代开始时继续这样做,直到StopIteration引发 a(这是迭代器协议)。所以,请注意:

In [2]: s = {1,2,3}

In [3]: it = iter(s)

In [4]: next(it)
Out[4]: 1

In [5]: s.add(1)

In [6]: next(it)
Out[6]: 2

In [7]: s.add(99)

In [8]: next(it)
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
<ipython-input-58-2cdb14c0d4d6> in <module>()
----> 1 next(it)

RuntimeError: Set changed size during iteration

正如错误所暗示的,当集合改变大小时,就会调用错误。我们可以通过以下方式实现这一点:

In [11]: class MyListIterator:
    ...:     def __init__(self, origin):
    ...:         self.origin = origin
    ...:         self.original_size = len(origin)
    ...:         self.i = 0
    ...:     def __iter__(self):
    ...:         return self
    ...:     def __next__(self):
    ...:         if len(self.origin) != self.original_size:
    ...:             raise RuntimeError("MyList changed size during iteration!")
    ...:         elif self.i == self.original_size:
    ...:             raise StopIteration
    ...:         x = self.origin.data[self.i]
    ...:         self.i += 1
    ...:         return x
    ...:
    ...: class MyList:
    ...:     def __init__(self):
    ...:         self.data = [1,2,3]
    ...:     def __iter__(self):
    ...:         return MyListIterator(self)
    ...:     def __len__(self):
    ...:         return len(self.data)
    ...:     def append(self, item):
    ...:         self.data.append(item)
    ...:

现在:

In [12]: mylist = MyList()

In [13]: for x in mylist:
    ...:     print(x)
    ...:
1
2
3

In [14]: for x in mylist:
    ...:     mylist.append(3)
    ...:     print(x)
    ...:
1
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
<ipython-input-14-3bd26e0c08b9> in <module>()
----> 1 for x in mylist:
      2     mylist.append(3)
      3     print(x)
      4

<ipython-input-14-f69ab7d03470> in __next__(self)
      8     def __next__(self):
      9         if len(self.origin) != self.original_size:
---> 10             raise RuntimeError("MyList changed size during iteration!")
     11         elif self.i == self.original_size:
     12             raise StopIteration

RuntimeError: MyList changed size during iteration!

请注意,您可以1在引发错误之前看到已打印,这是因为直到__next__在第二次迭代之前(或在第二次迭代开始时,但是您想考虑)才被 for 循环隐式调用它),错误被提出。


推荐阅读