如何在Python自定义迭代器类中重置索引属性?
你知道在Python中,当你遍历一个内置的可迭代对象,比如说一个list
时,索引每次都是从0开始的吗:
my_list = [1, 2, 3, 4, 5]
for item in my_list:
print(item)
for other_item in my_list:
print(item)
这段代码会打印出1
到5
两次。也就是说,当my_list
再次被遍历时,它是从列表的开头开始,而不是从5
开始。
我实现了一个自己的自定义迭代器类,结构大致是这样的(这是一个简化的例子,只包含与迭代相关的部分):
class MyIterator:
def __init__(self, my_list: list):
self.my_list = my_list
self._iteration_index = 0
def __iter__(self):
return self
def __next__(self):
element = self.my_list[self._iteration_index]
self._iteration_index += 1
if self._iteration_index >= len(self.my_list) - 1:
raise StopIteration
return element
目前,如果我创建一个MyIterator
类型的对象,我只能遍历一次。请问有没有地方可以把self._iteration_index
重新设置为0
,这样每次我遍历它时,都能从self.my_list
的开头开始呢?
我使用的是Python 3.11.7
2 个回答
-1
我觉得这个解决方案可以帮到你。
class MyIterator:
def __init__(self, my_list: list):
self.my_list = my_list
self._iteration_index = 0
def __iter__(self):
return self
def __next__(self):
if self._iteration_index >= len(self.my_list):
self._iteration_index = 0
raise StopIteration
element = self.my_list[self._iteration_index]
self._iteration_index += 1
return element
1
迭代器不允许像你想的那样重置:
一旦迭代器的
__next__()
方法抛出 StopIteration,它在后续的调用中必须继续抛出这个错误。那些不遵循这个规则的实现被认为是有问题的。
如果你想对你的对象进行两次迭代,你需要使用两个独立的迭代器。
使用两个不同的类。一个类表示你要迭代的对象;另一个类则表示对这个对象的迭代过程。
class MyIterable:
def __init__(self, my_list: list):
self.my_list = list
def __iter__(self):
return MyIterator(self)
class MyIterator:
def __init__(self, iterable):
self.list = iterable.list
self.index = 0
self.stopped = False
def __iter__(self):
return self
def __next__(self):
if self.stopped:
raise StopIteration
try:
x = self.list[self.index]
except IndexError:
self.stopped = True
raise StopIteration
self.index += 1
return x
这并不一定是 MyIterator
的最佳实现,但它展示了两个要点:
迭代器独立跟踪当前的索引,而不依赖于你正在迭代的对象。你可以为同一个对象创建多个同时存在的迭代器。
x = MyIterable([1,2,3]) i1 = MyIterator(x) i2 = MyIterator(x) assert next(i1) == 1 assert next(i2) == 1 assert next(i1) == 2
合适的迭代器在抛出
StopIteration
后绝不能再产生值。stopped
标志确保即使在之前尝试获取值后,底层列表被修改了,也不会出错。(在迭代器抛出StopIteration
之前修改底层列表也是个问题,这就是为什么通常建议不要修改你正在迭代的对象。)
不过,与其自己实现迭代器类,你可以把 __iter__
变成一个生成器函数,这样每次调用它时都会生成一个 新的 生成器,可以迭代你想要的任何东西。
class MyIterable:
def __init__(self, my_list: list):
self.my_list = list
def __iter__(self):
yield from self.my_list