Python中的迭代器和生成器是两个重要的概念,它们都是用于实现迭代操作的工具。迭代器是一个可以记住遍历的位置的对象;而生成器是一种特殊的迭代器,但是生成器更加强大。
一、迭代器
迭代器有两个基本的方法:
- iter()
- next()
到目前为止,大多数容器对象都可以使用 for 语句:
for element in [1, 2, 3]: print(element) for element in (1, 2, 3): print(element) for key in {'one':1, 'two':2}: print(key) for char in "123": print(char) for line in open("myfile.txt"): print(line, end='')
这种访问风格清晰、简洁又方便。 迭代器的使用非常普遍并使得 Python 成为一个统一的整体。 在幕后,for 语句会在容器对象上调用 iter()。 该函数返回一个定义了 __next__() 方法的迭代器对象,此方法将逐一访问容器中的元素。 当元素用尽时,__next__() 将引发 StopIteration 异常来通知终止 for 循环。 可以使用 next() 内置函数来调用 __next__() 方法;这个例子显示了它的运作方式:
>>>s = 'abc' >>>it = iter(s) >>>it <str_iterator object at 0x10c90e650> >>>next(it) 'a' >>>next(it) 'b' >>>next(it) 'c' next(it) Traceback (most recent call last): File "<stdin>", line 1, in <module> next(it) StopIteration
了解了迭代器协议背后的机制后,就可以轻松地为类添加迭代器行为了。 定义 __iter__() 方法用于返回一个带有 __next__() 方法的对象。 如果类已定义了 __next__(),那么 __iter__() 可以简单地返回 self:
class Reverse: """Iterator for looping over a sequence backwards.""" def __init__(self, data): self.data = data self.index = len(data) def __iter__(self): return self def __next__(self): if self.index == 0: raise StopIteration self.index = self.index - 1 return self.data[self.index]
>>>rev = Reverse('spam') >>>iter(rev) <__main__.Reverse object at 0x00A1DB50> >>>for char in rev: ... print(char) ... m a p s
二、生成器
生成器是一个用于创建迭代器的简单而强大的工具。 它们的写法类似于标准的函数,但当它们要返回数据时会使用 yield 语句。 每次在生成器上调用 next() 时,它会从上次离开的位置恢复执行(它会记住上次执行语句时的所有数据值)。 一个显示如何非常容易地创建生成器的示例如下:
def reverse(data): for index in range(len(data)-1, -1, -1): yield data[index]
>>>for char in reverse('golf'): ... print(char) ... f l o g
生成器的写法更为紧凑,因为它会自动创建 __iter__() 和 __next__() 方法。另一个关键特性在于局部变量和执行状态会在每次调用之间自动保存。该函数相比使用 self.index 和 self.data 这种实例变量的方式更易编写且更为清晰。除了会自动创建方法和保存程序状态,当生成器终结时,它们还会自动引发 StopIteration。 这些特性结合在一起,使得创建迭代器能与编写常规函数一样容易。
三、生成器表达式
某些简单的生成器可以写成简洁的表达式代码,所用语法类似列表推导式,但外层为圆括号而非方括号。 这种表达式被设计用于生成器将立即被外层函数所使用的情况。 生成器表达式相比完整的生成器更紧凑但较不灵活,相比等效的列表推导式则更为节省内存。
示例:
>>>sum(i*i for i in range(10)) # sum of squares 285 >>>xvec = [10, 20, 30] >>>yvec = [7, 5, 3] >>>sum(x*y for x,y in zip(xvec, yvec)) # dot product 260 >>>unique_words = set(word for line in page for word in line.split()) >>>valedictorian = max((student.gpa, student.name) for student in graduates) >>>data = 'golf' >>>list(data[i] for i in range(len(data)-1, -1, -1)) ['f', 'l', 'o', 'g']