python学习之生成器yield
yield的作用是使函数生成一个结果序列而不是一个值,任何使用yield的函数都称为生成器,调用生成器会创建一个对象,该对象通过连续调用next()或者__next__()方法生成结果序列
一般情况
>>> def count(n,m):
>>> print('这是一个循环外部测试')
>>> while n>0:
>>> print('这是一个循环内部测试')
>>> yield n,m
>>> n-=1
>>> c=count(5,2)
>>> c.__next__()
这是一个循环外部测试
这是一个循环内部测试
5,2
首先我定义了生成器,利用了yield关键词,但是注意,我没有像其它列子一样使用单个参数,yield的后面是两个参数,这里就是为了提醒,yield后面可以跟多个参数,他的后面参数表示要传给yield的这个关键词的参数,甚至可以用表达式,但是一定要用括号括起来,比如yield (n+3),这样的参数也可以
当初次调用count的时候,该函数什么也不做,因为他知道这是个yield的函数,所以也不会打印“这是一个外部循环测试"这段文字,但是,当调用__next__()之后,函数开始执行,执行到yield关键词,然后停止执行,函数的返回值就是要传给yield关键词的参数(其实用yield关键词相当于古时候的驿站,参数过来歇歇脚,然后继续上路,每遇到一次yield就要歇脚一次,调用一次__next__(),相当于开始赶路一次),就是如上图返回的5,2
当再次执行时,该函数从yield处开始执行,执行减一操作,然后继续循环,遇到yield停止
>>> c.__next__()
这是一个循环内部测试
4,2
>>> c.__next__()
这是一个内部循环测试
3,2
>>> c.__next__()
这是一个内部循环测试
2,2
下面一个python参考手册上的一个列子
利用pyhton实现linux上的tail -f |grep python这个命令
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
wwwlog=tail(open('access-log'))
pylines=grep(wwwlog,'python')
for line in pylines:
print(line)
协程与yield表达式
当yield表达式在右边时,这样的函数称为协程,它的执行是为了响应它的值
def receiver():
print('Ready to receive....')
while True:
n = (yield)
print('Got %s'%n)
上面的(yield)表达式接收该函数用send()发来的参数,赋值给n
>>> r=receiver() #形成一个协程,函数不执行
>>> r.__next__() #函数执行,直到遇到yield表达式
Ready to receive....
>>> r.send(1)
Got 1
>>> r.send('Hello')
Got Hello
>>>
这里的__next__()初始调用是必须的,这样协程才会挂起,直到对象利用send()方法传递一个值
如果想关闭协程,可以使用close()方法
>>> r.close()
>>>
注意:这里面n=(yield)表达式也可以将在yield后面加上返回值,如n=(yield result),这样该函数就有返回值
生成器与协程的用法
逆序使用生成器
import os
import fnmatch
def find_files(topdir,pattern):
for path ,dirname,filelist in os.walk(topdir):
for name in filelist:
if fnmatch.ffnmatch(name,pattern):
yield os.path.join(path,name)
import gzip,bz2
def opener(filenames):
for name in filenames:
if name.endswith(".gz"):f.gzip.open(name)
elif name.endswith(".bz2"):f=bz2.BZ2File(name)
else: f=open(name)
yield f
def cat(filelist):
for f in filelist:
for line in f:
yield line
def grep(pattern,lines):
for line in lines:
if pattern in line:
yield line
wwwlogs=find_files('www','access-log*')
files=opener(wwwlogs)
lines=cat(files)
pylines=grep('python',lines)
for line in pylines:
sys.stdout.write(line)
顺序使用协程
import os,fnmatch
#建立一个装饰器,将每次协程必须调用的next方法自动化
def coroutine(func):
def start(*args,**kwargs):
g=func(*args,**kwargs)
g.__next__()
return g
return start
@coroutine
def find_files(target):
while True:
topdir,pattern= (yield)
for path,dirname,filelist in os.walk(topdir):
for name in filelist:
if fnmatch.fnmatch(name,pattern):
target.send(os.path.join(path,name))
import gzip,bz2
@coroutine
def opener(target):
while True:
name=(yield)
if name.endswith('.gz'):f=gzip.open(name)
elif name.endswith('.bz2'):f=bz2.BZ2File(name)
else: f=open(name)
target.send(f)
@coroutine
def cat(target):
while True:
f=(yield)
for line in f:
targe.send(line)
@coroutine
def grep(pattern,target):
while True:
line =(yield)
if pattern in line:
target.send(line)
@coroutine
def printer():
while True:
line =(yield)
sys.stdout.write(line)
finder=find_files(opener(cat(grep('python',printer()))))
finder.send(('www','access-log*')) #发送的是一个整体,所以用括号