迭代协议
如下,调用__iter__()建立迭代器对象并调用__next__()方法迭代元素。
>>> x = [1,2,3]
>>> it = x.__iter__()
>>> it
<listiterator object at 0x590b0>
>>> it.__next__()
1
>>> it.__next__()
2
>>> it.__next__()
3
>>> it.__next__()
Traceback (most recent call last):
File "<stdin>", line 1, in ? StopIteration
>>>
常用的 for-loop 迭代使用了底层的迭代协议。
生成器
生成器是使用 yield 定义迭代的任意函数。
def countdown(n):
# Added a print statement
print('Counting down from', n)
while n > 0:
yield n
n -= 1
调用生成器会建立一个生成器对象,并不会马上输出结果。
>>> x = countdown(10)
# There is NO PRINT STATEMENT
>>> x
# x is a generator object
<generator object at 0x58490>
>>>
生成器使用__next__() 迭代输出。
>>> x = countdown(10)
>>> x
<generator object at 0x58490>
>>> x.__next__()
Counting down from 10
10
>>>
yield产生一个值,但暂停函数执行,该函数恢复执行在下一次调用__next__()时。
生成器函数实现了与for语句在列表、元组、字典、文件等使用中,相同的低级迭代协议。
生成器推导式
列表推导式的生成器版本。
>>> a = [1,2,3,4]
>>> b = (2*x for x in a)
>>> b
<generator object at 0x58760>
>>> for i in b:
... print(i, end=' ')
...
2 4 6 8
>>>
与列表推导式的区别:
- 不构造列表。
- 构建生成器对象,唯一的目的是迭代。
- 不可重复使用。
不可重复使用的意思如下:
>>> nums = [1, 2, 3, 4, 5]
>>> squares = (x*x for x in nums)
>>> squares
<generator object <genexpr> at 0x109207e60>
>>> for n in squares:
... print(n)
...
1
4
9
16
25
如果再尝试 for 循环遍历,将没有结果。
>>> for n in squares:
... print(n)
...
>>>
语法结构,注意是括号。
(<expression> for i in s if <conditional>)
小型生成器的编写例子:
def filter_symbols(rows, names):
for row in rows:
if row['name'] in names:
yield row
应使用如下方式编写:
rows = (row for row in rows if row['name'] in names)
生成器表达式的主要用途是对序列执行一些计算,但只使用一次结果。
使用生成器,代码运行得更快并且使用的内存更少。
使用生成器的好处:
- 在迭代时,可以执行搜索,替换,修改等操作。
- 处理管道可以应用于多种数据处理问题。
- 仅在需要的时候 yield 值,可以对流数据进行操作,与列表推导式相比不需要构建列表,节省了内存。
- 生成器代码可重复使用,并且迭代与使用迭代的功能被分开。
itertools 模块
itertools是一个库模块,具有旨在帮助迭代器/生成器的各种功能。
一些例子
itertools.chain(s1,s2)
itertools.count(n)
itertools.cycle(s)
itertools.dropwhile(predicate, s)
itertools.groupby(s)
itertools.ifilter(predicate, s)
itertools.imap(function, s1, ... sN)
itertools.repeat(s, n)
itertools.tee(s, ncopies)
itertools.izip(s1, ... , sN)
所有函数都以迭代方式处理数据。
更多内容查看生成器实用技巧。
生产者和消费者模型
生成器与各种形式的生产者-消费者问题密切相关,类似 Unix 管道。
# Producer
def follow(f):
...
while True:
...
yield line # Produces value in `line` below
...
# Consumer
for line in follow(f): # Consumes value from `yield` above
...
yield 为消费者提供价值。
管道
生产者→加工→加工→消费者。
管道有一个初始数据生产者、一组中间处理阶段和一个最终消费者。
def producer():
...
yield item # yields the item that is received by the `processing`
...
def processing(s):
for item in s: # Comes from the `producer`
...
yield newitem # yields a new item
...
def consumer(s):
for item in s: # Comes from the `processing`
...
a = producer()
b = processing(a)
c = consumer(b)