Python之美[从菜鸟到高手]--生成器之全景分析
来源:互联网 发布:java 打印所有header 编辑:程序博客网 时间:2024/05/18 02:03
yield指令,可以暂停一个函数并返回中间结果。使用该指令的函数将保存执行环境,并且在必要时恢复。
生成器比迭代器更加强大也更加复杂,需要花点功夫好好理解贯通。
看下面一段代码:
def gen(): for x in xrange(4): tmp = yield x if tmp == 'hello': print 'world' else: print str(tmp)
只要函数中包含yield关键字,该函数调用就是生成器对象。
g=gen()print g #<generator object gen at 0x02801760>print isinstance(g,types.GeneratorType) #True我们可以看到,gen()并不是函数调用,而是产生生成器对象。
生成器对象支持几个方法,如gen.next() ,gen.send() ,gen.throw()等。
print g.next() # 0调用生成器的next方法,将运行到yield位置,此时暂停执行环境,并返回yield后的值。所以打印出的是0,暂停执行环境。
print g.next() #None 1再调用next方法,你也许会好奇,为啥打印出两个值,不急,且听我慢慢道来。
上一次调用next,执行到yield 0暂停,再次执行恢复环境,给tmp赋值(注意:这里的tmp的值并不是x的值,而是通过send方法接受的值),由于我们没有调用send方法,所以
tmp的值为None,此时输出None,并执行到下一次yield x,所以又输出1.
到了这里,next方法我们都懂了,下面看看send方法。
print g.send('hello') #world 2上一次执行到yield 1后暂停,此时我们send('hello'),那么程序将收到‘hello',并给tmp赋值为’hello',此时tmp=='hello'为真,所以输出'world',并执行到下一次yield 2,所以又打印出2.(next()等价于send(None))
当循环结束,将抛出StopIteration停止生成器。
看下面代码:
def stop_immediately(name): if name == 'skycrab': yield 'okok' else: print 'nono's=stop_immediately('sky')s.next()正如你所预料的,打印出’nono',由于没有额外的yield,所以将直接抛出StopIteration。
nonoTraceback (most recent call last): File "F:\python workspace\Pytest\src\cs.py", line 170, in <module> s.next()StopIteration看下面代码,理解throw方法,throw主要是向生成器发送异常。
def mygen(): try: yield 'something' except ValueError: yield 'value error' finally: print 'clean' #一定会被执行gg=mygen()print gg.next() #somethingprint gg.throw(ValueError) #value error clean调用gg.next很明显此时输出‘something’,并在yield ‘something’暂停,此时向gg发送ValueError异常,恢复执行环境,except 将会捕捉,并输出信息。
理解了这些,我们就可以向协同程序发起攻击了,所谓协同程序也就是是可以挂起,恢复,有多个进入点。其实说白了,也就是说多个函数可以同时进行,可以相互之间发送消息等。
这里有必要说一下multitask模块(不是标准库中的),看一段multitask使用的简单代码:
def tt(): for x in xrange(4): print 'tt'+str(x) yielddef gg(): for x in xrange(4): print 'xx'+str(x) yieldt=multitask.TaskManager()t.add(tt())t.add(gg())t.run()
结果:
tt0xx0tt1xx1tt2xx2tt3xx3
如果不是使用生成器,那么要实现上面现象,即函数交错输出,那么只能使用线程了,所以生成器给我们提供了更广阔的前景。
如果仅仅是实现上面的效果,其实很简单,我们可以自己写一个。主要思路就是将生成器对象放入队列,执行send(None)后,如果没有抛出StopIteration,将该生成器对象再加入队列。
class Task(): def __init__(self): self._queue = Queue.Queue() def add(self,gen): self._queue.put(gen) def run(self): while not self._queue.empty(): for i in xrange(self._queue.qsize()): try: gen= self._queue.get() gen.send(None) except StopIteration: pass else: self._queue.put(gen)t=Task()t.add(tt())t.add(gg())t.run()
当然,multitask实现的肯定不止这个功能,有兴趣的童鞋可以看下源码,还是比较简单易懂的。
#增补 2014/5/21
之前我在南京面试Python时遇到这么一道题目:
def thread1(): for x in range(4): yield x def thread2(): for x in range(4,8): yield x threads=[]threads.append(thread1())threads.append(thread2())def run(threads): #写这个函数,模拟线程并发 passrun(threads)如果上面class Task看懂了,那么这题很简单,其实就是考你用yield模拟线程调度,解决如下:
def run(threads): for t in threads: try: print t.next() except StopIteration: pass else: threads.append(t)
- Python之美[从菜鸟到高手]--生成器之全景分析
- Python之美[从菜鸟到高手]--生成器之全景分析
- Python之美[从菜鸟到高手]--生成器之全景分析
- Python之美[从菜鸟到高手]--生成器之全景分析
- Python之美[从菜鸟到高手]--生成器之全景分析
- Python之美[从菜鸟到高手]--urllib源码分析
- Python之美[从菜鸟到高手]--urlparse源码分析
- Python之美[从菜鸟到高手]--装饰器之使用情景分析
- Python之美[从菜鸟到高手]--装饰器之使用情景分析
- Python之美[从菜鸟到高手]--NotImplemented小析
- Python之美[从菜鸟到高手]--NotImplemented小析
- Python之美[从菜鸟到高手]--NotImplemented小析
- Python之美[从菜鸟到高手]--NotImplemented小析
- Python之美[从菜鸟到高手]--httplib源码分析及IncompleteRead异常解决方案
- Python之美[从菜鸟到高手]--浅拷贝、深拷贝完全解读(copy源码分析)
- 【转】Python之美[从菜鸟到高手]--urlparse源码分析
- Java之美[从菜鸟到高手演变]之字符串
- Java之美[从菜鸟到高手演变]之智力题
- HTML之<!DOCTYPE> 标签
- win8安装oracle10g客户端
- 分支限界法
- GBK转unicode码查询表的改进
- sharepoint 2013 文档库 资源管理器打开报错 在文件资源管理器中打开此位置时遇到问题,将此网站添加到受信任站点列表,然后重试。
- Python之美[从菜鸟到高手]--生成器之全景分析
- ASP.NET基础知识
- Bat 循环执行
- Select CASE
- asp生成word
- Oracle数据库 ORA-01555 快照过旧 (undo表空间:撤销表空间)
- linux硬件设备操作函数 open(/dev/ietctl, O_RDWR|...)
- HTML5新特性之CSS+HTML5实例
- linux 下查看cpu位数及内核参数