python15:迭代器和生成器

迭代器和生成器都是Python提供的强大工具,可以帮助用户写出优雅的代码。下面介绍迭代器和生成器的使用方法和内部机制。

迭代器

在Pyton中,大部分的容器对象都能用于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()方法得到容器的迭代器,Python要求迭代器定义了方法__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()被调用,产生器从停下来的地方重新开始(它会记住所有的数据值和最后执行的语句)。下面是一个产生器的例子:

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__()方法被自动生成了。
产生器的另一个好处是本地变量和执行描述在两次调用间自动保存,因此产生器更容易写,也比使用那个实例变量更加清晰。
在自动生成方法和保存状态之外,当产生器中止时,他们自动抛出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

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

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。