特殊方法
__iter__和
__next__是创建迭代器类型的迭代器协议的一部分。为此,您必须区分两个独立的事物:
Iterables 和 iterators 。
Iterables 有些事情可以重复,通常情况下,这些都是某种包含物品的容器元素。常见的例子是列表,元组或字典。
为了迭代可迭代对象,您可以使用 iterator 。迭代器是帮助您遍历容器的对象。例如,当迭代列表时,迭代器本质上会跟踪您当前所处的索引。
为了获得一个迭代器,在迭代器
__iter__上调用该方法。这就像一个工厂方法,将为此特定可迭代方法返回一个新的迭代器。具有
__iter__定义的方法的类型将其转换为可迭代的。
迭代器通常需要单个方法,
__next__该方法返回迭代的 下
一项。另外,为了使协议更易于使用,每个迭代器也应该是可迭代的,并在
__iter__方法中返回自身。
作为一个简单的示例,这可能是列表的迭代器实现:
class ListIterator: def __init__ (self, lst): self.lst = lst self.idx = 0 def __iter__ (self): return self def __next__ (self): try: item = self.lst[self.idx] except IndexError: raise StopIteration() self.idx += 1 return item
然后,列表实现可以简单地
ListIterator(self)从
__iter__方法中返回。当然,列表的实际实现是在C中完成的,因此看起来有些不同。但是想法是一样的。
迭代器在Python中的各个地方都被不可见地使用。例如一个
for循环:
for item in lst: print(item)
这与以下内容相同:
lst_iterator = iter(lst) # this just calls `lst.__iter__()`while True: try: item = next(lst_iterator) # lst_iterator.__next__() except StopIteration: break else: print(item)
因此,for循环从可迭代对象请求一个迭代器,然后对其进行调用
__next__,直到遇到
StopIteration异常为止。这是在表面下发生的,这也是您还希望迭代器也实现的原因
__iter__:否则,您将永远无法遍历迭代器。
对于生成器,人们通常所指的实际上是生成器 函数 ,即具有
yield语句的某些函数定义。调用该生成器函数后,您将返回一个 generator
。生成器本质上只是一个迭代器,尽管它是花哨的(因为它比在容器中移动要多)。作为迭代器,它具有
__next__“生成”下一个元素的
__iter__方法和返回自身的方法。
生成器函数的示例如下:
def exampleGenerator(): yield 1 print('After 1') yield 2 print('After 2')包含
yield语句的函数主体将其转换为生成器函数。这意味着在调用时,
exampleGenerator()您将返回一个 生成器
对象。Generator对象实现了迭代器协议,因此我们可以对其进行调用
__next__(或使用
next()上面的函数):
>>> x = exampleGenerator()>>> next(x)1>>> next(x)After 12>>> next(x)After 2Traceback (most recent call last): File "<pyshell#10>", line 1, in <module> next(x)StopIteration
请注意,第一个
next()呼叫尚未打印任何内容。这是关于生成器的特殊之处:它们是惰性的,并且仅评估必要的内容以从迭代器中获取下一项。只有第二次
next()调用,我们才能从函数体中获得第一条打印行。而且我们需要另一个
next()调用来耗尽可迭代对象(因为没有其他值可产生)。
但是除了这种懒惰之外,生成器的行为就像可迭代的一样。
StopIteration最后,您甚至会遇到一个异常,该异常允许将生成器(和生成器函数)用作
for循环源,并在任何可以使用“常规”可迭代项的地方使用。
发电机及其懒惰的最大好处是可以 按需
生产东西。一个很好的类比是在网站上无休止地滚动:您可以在之后(调用
next()生成器)之后向下滚动项目,并且每隔一段时间,网站将不得不查询后端以检索更多项目以供您滚动浏览。理想情况下,这种情况不会引起您的注意。而这正是发电机的作用。它甚至允许这样的事情:
def counter(): x = 0 while True: x += 1 yield x
非延迟的,由于这是一个无限循环,因此无法计算。但是懒惰地,作为生成器,有可能在一个项目之后消耗一个迭代的项目。我本来想让您不必将此生成器实现为完全自定义的迭代器类型,但是在这种情况下,这实际上并不是太困难,所以就来了:
class CounterGenerator: def __init__ (self): self.x = 0 def __iter__ (self): return self def __next__ (self): self.x += 1 return self.x



