列表和集合之间的区别之一是列表可以在迭代期间更改 - 我们可以在循环中附加到它,等等。但是,如果我们尝试在 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__ 以便它会像集合一样引发运行时错误?
最佳答案
进入 for 循环时,Python 首先对可迭代对象调用 iter
来获取或创建一个迭代器。然后,循环从迭代器中请求下一个项目,直到看到 StopIteration
异常(除非流程已通过 break
提前退出循环, return
语句,或其他一些异常)。 for 循环例如:
for element in iterable:
...
可以大致这样重写:
it = iter(iterable)
while True:
try:
element = next(it)
except StopIteration:
break
...
现在,通过使用列表实例作为可迭代对象,您将迭代与使用集合实例作为可迭代对象时不同的迭代器类型。/em>:
>>> iter([0])
<list_iterator at 0xcafef00d>
>>> iter({0})
<set_iterator at 0xdeadbeef>
set_iterator
类型和 list_iterator
类型以不同的方式实现__next__
。这是setiter_iternext
CPython 中的函数,其中 changing size is guarded against 。 listiter_next
没有这样的守卫。
我希望您现在可以了解如何直接在 Python 迭代器中创建类似的保护措施。当您定义__next__
方法时,您可以检查大小是否已更改并引发:
class MyIterator:
def __init__(self, obj):
self.obj = obj # note: you may prefer to use a weakref here
self.it = iter(obj)
self.initial_size = len(obj)
def __iter__(self):
return self
def __next__(self):
if len(self.obj) != self.initial_size:
raise RuntimeError('changed size...doh!')
return next(self.it)
class GrumpyList:
def __init__(self, data):
self.data = data
def __iter__(self):
return MyIterator(self.data)
演示:
>>> for i in g:
... print(i)
... if i == 2:
... g.data.append(99)
...
0
1
2
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
...
RuntimeError: changed size...doh!
关于python - 迭代期间运行时错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50166773/