你可能已经意识到了,大多数的容器对象都是可以使用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
生成器是创建迭代器简单而有效的工具。它的写法和普通的函数类似,但是在返回数据的时候使用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.index
和 self.data
这样的实例变量要简洁的多。
在生成器中断的时候除了能够自动创建方法、保存程序状态之外,也会自动引发StopIteration异常。总结一下,这些特性使得创建一个和普通函数没有区别的迭代器变得容易。
一些简单的生成器可以编写的像表达式一样简洁,只需要使用一种类似于列表生成式(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']