摘自the Python Practice book:
5。迭代器和生成器
5.1。迭代器
我们使用 for 语句循环列表。
>>> for i in [1, 2, 3, 4]:
... print i,
...
1
2
3
4
如果我们将它与字符串一起使用,它会遍历其字符。
>>> for c in "python":
... print c
...
p
y
t
h
o
n
如果我们将它与字典一起使用,它会遍历其键。
>>> for k in {"x": 1, "y": 2}:
... print k
...
y
x
如果我们将它与文件一起使用,它会遍历文件的行。
>>> for line in open("a.txt"):
... print line,
...
first line
second line
因此,有许多类型的对象可以与 for 循环一起使用。这些被称为可迭代对象。
有许多函数使用这些可迭代对象。
>>> ",".join(["a", "b", "c"])
'a,b,c'
>>> ",".join({"x": 1, "y": 2})
'y,x'
>>> list("python")
['p', 'y', 't', 'h', 'o', 'n']
>>> list({"x": 1, "y": 2})
['y', 'x']
5.1.1。迭代协议
内置函数 iter 接受一个可迭代对象并返回一个迭代器。
>>> x = iter([1, 2, 3])
>>> x
<listiterator object at 0x1004ca850>
>>> x.next()
1
>>> x.next()
2
>>> x.next()
3
>>> x.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
停止迭代
每次我们调用迭代器的下一个方法时,都会给我们下一个元素。如果没有更多元素,则会引发 StopIteration。
迭代器被实现为类。这是一个类似于内置 xrange 函数的迭代器。
class yrange:
def __init__(self, n):
self.i = 0
self.n = n
def __iter__(self):
return self
def next(self):
if self.i < self.n:
i = self.i
self.i += 1
return i
else:
raise StopIteration()
iter 方法使对象可迭代。在幕后,iter 函数在给定对象上调用 iter 方法。
iter 的返回值是一个迭代器。它应该有一个 next 方法并在没有更多元素时引发 StopIteration。
让我们试试吧:
>>> y = yrange(3)
>>> y.next()
0
>>> y.next()
1
>>> y.next()
2
>>> y.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 14, in next
停止迭代
许多内置函数接受迭代器作为参数。
>>> list(yrange(5))
[0, 1, 2, 3, 4]
>>> sum(yrange(5))
10
在上面的例子中,iterable 和 iterator 都是同一个对象。请注意,iter 方法返回了 self。不一定总是这样。
class zrange:
def __init__(self, n):
self.n = n
def __iter__(self):
return zrange_iter(self.n)
class zrange_iter:
def __init__(self, n):
self.i = 0
self.n = n
def __iter__(self):
# Iterators are iterables too.
# Adding this functions to make them so.
return self
def next(self):
if self.i < self.n:
i = self.i
self.i += 1
return i
else:
raise StopIteration()
如果可迭代和迭代器都是同一个对象,则在一次迭代中消耗它。
>>> y = yrange(5)
>>> list(y)
[0, 1, 2, 3, 4]
>>> list(y)
[]
>>> z = zrange(5)
>>> list(z)
[0, 1, 2, 3, 4]
>>> list(z)
[0, 1, 2, 3, 4]
5.2。发电机
生成器简化了迭代器的创建。生成器是一个生成一系列结果而不是单个值的函数。
def yrange(n):
i = 0
while i < n:
yield i
i += 1
每次执行 yield 语句时,函数都会生成一个新值。
>>> y = yrange(3)
>>> y
<generator object yrange at 0x401f30>
>>> y.next()
0
>>> y.next()
1
>>> y.next()
2
>>> y.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
停止迭代
所以生成器也是迭代器。您不必担心迭代器协议。
“生成器”这个词被混淆地用来表示生成的函数和它生成的内容。在本章中,我将使用“生成器”一词来表示生成的对象,而“生成器函数”则表示生成它的函数。
你能想一想它在内部是如何运作的吗?
当调用生成器函数时,它甚至没有开始执行该函数就返回一个生成器对象。当第一次调用 next 方法时,函数开始执行直到它到达 yield 语句。产生的值由下一次调用返回。
以下示例演示了 yield 和对生成器对象的 next 方法的调用之间的相互作用。
>>> def foo():
... print "begin"
... for i in range(3):
... print "before yield", i
... yield i
... print "after yield", i
... print "end"
...
>>> f = foo()
>>> f.next()
begin
before yield 0
0
>>> f.next()
after yield 0
before yield 1
1
>>> f.next()
after yield 1
before yield 2
2
>>> f.next()
after yield 2
end
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
停止迭代
让我们看一个例子:
def integers():
"""Infinite sequence of integers."""
i = 1
while True:
yield i
i = i + 1
def squares():
for i in integers():
yield i * i
def take(n, seq):
"""Returns first n values from the given sequence."""
seq = iter(seq)
result = []
try:
for i in range(n):
result.append(seq.next())
except StopIteration:
pass
return result
print take(5, squares()) # prints [1, 4, 9, 16, 25]