Python中的生成器,协程与yield

生成器与yield

1
2
3
4
5
6
def countdown(n):
    print("Counting down from %d"%n)
    while n>0:
        yield n
        n-=1
    return

语句yield使得函数返回一个生成器,每次调用next()时。生成器函数将不断执行语句,直至遇到yield语句。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
>>>c=countdown(10)
>>>c.next()
Counting down from 10
>>>c.next()
Counting down from 9
# 协程与yield
def receiver():
    print("Ready to receive")
    while True:
        n=(yield)
        print("got %s"%n)
>>>r=receiver()
>>>r.next()
Ready to receive
>>>r.send(1)
got 1
>>>r.send(2)
got 2
>>>r.send("Hello")
got Hello

这种使用yield语句的函数成为协程,它的执行是为了响应发送给它的值。
每次开始的next()函数时必不可少的
可以编写一个装饰器

1
2
3
4
5
6
7
8
9
10
11
12
def coroutine(func):
    def start(*args,**kwargs):
        g=func(*args,**kwargs)
        g.next()
        return g
    return start
@coroutine
def receiver():
    print("Ready to receive")
    while True:
        n=(yield)
        print("got %s"%n)

可用.close()关闭。关闭后如果继续调用.send()就会引发StopIteration异常

1
>>>r.close()

可以使用throw(exctype[,value[,tb]])方法在协程内部引发异常。
exctype是指异常类型
value是指异常的值
tb是指跟踪对象

1
>>>r.throw(RuntimeError,"You're hosed!")

协程还可以同时接受和发出返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
def line_splitter(delimiter=None):
    print("Ready to splite")
    result=None
    while True:
        line=(yield result)
        result=line.splite(delimiter)
>>>s=line_splitter(",")
>>>s.next()
Ready to splite
>>>s.send("A,B,C")
['A','B','C']
#  应用
import os
import fnmatch
def find_files (topdir, pattern):
    for path, dirname, filelist in os.walk(topdir):
        for name in filelist:
            if fnmatch.frunatch(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:
    print line

在这个例子中,程序要处理的是顶级目录“www”的所有子目录中的所有“access-log”文件中的所有行。程序将测试每个”access-log”文件的文件压缩情况,然后使用正确的文件打开器打开它们。程序将各行连接在一起,并通过查找子字符串”python”的过滤器进行处理。整个程序是由位于最后的for语句驱动的。该循环的每次迭代都会通过管道获得一个新值并使用之。此外,这种实现占用内存极少,因为它无需创建任何临时列表或其他大型的数据结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import os
import fnmatch
@coroutine
def find_files(target):
    while True:
        topdir, pattern=(yield)
        for path, dirname, filelist in os.walk(topdir):
            for name in filelist:
                if fn.rnatch. fn.match (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:
            target.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)
    print(line)
finder=find_files(opener(cat(grep("python",printer()))))
finder.send(("www","access-log"))

在这个例子中每个协程都发送数据给在它们的target参数中指定的另一个协程。和生成器的例子不同,执行完全由將数据发送到第一个协程find_files()中来驱动。接下来,这个协程将数据转人下一阶段。这个例子有一个关键的地方,即协程管道永远保持活动状态,直到它显式调用close()方法为止。为此,只要需要,程序可以不断地给协程中注人数据。

文章目录
  1. 1. 生成器与yield
|