9.9 迭代器(Iterators)

    你可能已经意识到了,大多数的容器对象都是可以使用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语句幕后就是对容器对象调用的itor()。这个函数返回一个迭代器对象,它定义了方法_next_()在每次调用时访问容器中的一个元素。当没有更多元素时,_next_()引发一个StopIteration异常,来告诉for循环终止遍历。你可以使用内建函数next()来调用_next_()方法;这个例子展示了它的所有工作内容:

>>> s = 'abc'
>>> it = iter(s)
>>> it
<iterator object at 0x00A1DB50>
>>> next(it)
'a'
>>> next(it)
'b'
>>> next(it)
'c'
>>> next(it)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
    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

9.10 生成器(Generators)

    生成器是创建迭代器简单而有效的工具。它的写法和普通的函数类似,但是在返回数据的时候使用yield语句。next()每次调用到它的时候,生成器就会从它中断的地方恢复执行(resume)(它会记录下所有数据的值和最后执行的一条语句)。用一个例子来展示创建生成器是多么简单:

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.indexself.data这样的实例变量要简洁的多。

    在生成器中断的时候除了能够自动创建方法、保存程序状态之外,也会自动引发StopIteration异常。总结一下,这些特性使得创建一个和普通函数没有区别的迭代器变得容易。

9.10 生成器表达式(Generator Expressions)

    一些简单的生成器可以编写的像表达式一样简洁,只需要使用一种类似于列表生成式(list comprehensions)的语法,但是要把中括号[]替换成小括号()。这些表达式是为封闭函数需要立即使用生成器的情况而设计的。生成器表达式更简洁但是和完整的生成器定义相比功能较弱,它往往比等价的列表生成式更节约内存。

    例子:

>>> 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

>>> from math import pi, sin
>>> sine_table = {x: sin(x*pi/180) for x in range(0, 91)}

>>> 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']