生成器与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 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异常
可以使用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()方法为止。为此,只要需要,程序可以不断地给协程中注人数据。