本节书摘来自异步社区《Python参考手册(第4版•修订版)》一书中的第1章,第1.12节,作者David M. Beazley,更多章节内容可以访问云栖社区“异步社区”公众号查看。
1.12 生成器
如果使用yield语句,可以让函数生成一个结果序列,而不仅仅是一个值,例如:
def countdown(n):
print "Counting down!"
while n > 0:
yield n # 生成一个值(n)
n -= 1
任何使用yield的函数都称为生成器。调用生成器函数将创建一个对象,该对象通过连续调用next()方法(在Python 3中是__next__())生成一系列的结果,例如:
>>> c = countdown(5)
>>> c.next()
Counting down!
5
>>> c.next()
4
>>> c.next()
3
>>>
next()调用使生成器函数一直运行,到下一条yield语句为止。此时next()将返回传递给yield的值,而且函数将暂时中止执行。再次调用next()时,函数将继续执行yield之后的语句。此过程持续到函数返回为止。
通常不会像上面这样手动调用next(),而是会使用一个for循环,例如:
>>> for i in countdown(5):
... print i,
Counting down!
5 4 3 2 1
>>>
生成器是编写基于处理管道、流或数据流程序的一种极其强大的方式。例如,下面的生成器函数模拟了常用于监控日志文件的UNIX tail –f命令的行为:
# tail一个文件(如tail -f)
import time
def tail(f):
f.seek(0,2) # 移动到EOF
while True:
line = f.readline() # 尝试读取一个新的文本行
if not line: # 如果没有内容,暂时休眠并再次尝试
time.sleep(0.1)
continue
yield line
下面的生成器用于在很多行中查找特定的子字符串:
def grep(lines, searchtext):
for line in lines:
if searchtext in line: yield line
下面的例子将以上两个生成器合并在一起,创建了一个简单的处理管道:
# UNIX "tail –f | grep python"命令的python实现
wwwlog = tail(open("access-log"))
pylines = grep(wwwlog,"python")
for line in pylines:
print line,
生成器的微妙之处在于,它经常和其他可迭代的对象(如列表或文件)混合在一起。特别是在编写如for item in s这样的语句时,s可以代表一个列表、文件的各行、生成器函数的结果,或者支持迭代的其他任何对象。能够在s中插入不同对象,为创建可扩展的程序提供了一个强大的工具。