欢迎各位兄弟 发布技术文章

这里的技术是共享的

You are here

协程与yield表达式

在函数内,yield语句还可以用作出现在赋值运算符右边的表达式,例如:
[python] view plain copy
 
  1. def receiver():  
  2.     print "Ready to receive"  
  3.     while True:  
  4.         n = (yield)  
  5.         print "Got %s" % n  

    以这种方式使用yield语句的函数称为协程,它的执行是为了响应发送给它的值。它的行为也十分类似于生成器,例如:

[python] view plain copy
 
  1. >>> r = receiver()  
  2. >>> r.next()   #向前执行到第一条yield语句(在Python3中是r.__next__())  
  3. Ready to receive  
  4. >>> r.send(1)  
  5. Got 1  
  6. >>> r.send(2)  
  7. Got 2  
  8. >>> r.send("Hello")  
  9. Got Hello  
  10. >>>  

    在这个例子中,对next()的初始调用是必不可少的,这样协程才能执行可通向第一个yield表达式的语句。在这里协程会挂起,等待相关生成器对象r的send()方法给它发送一个值。传递给send()的值由协程中的(yield)表达式返回。接收到值后,协程就会执行语句,直至遇到下一条yield语句。
    在协程中需要调用next()这件事情很容易被忽略,经常成为错误出现的根源。因此,建议使用一个能自动完成该步骤的装饰器来包装协程。

[python] view plain copy
 
  1. def coroutine(func):  
  2.     def start(*args, **kwargs)  
  3.         g = func(*args, **kwargs)  
  4.         g.next()  
  5.         return g  
  6.     return start  

    使用这个装饰器就可以像下面这样编程和使用协程:

[python] view plain copy
 
  1. @coroutine  
  2. def receiver():  
  3.     print "Ready to receive"  
  4.     while True:  
  5.         n = (yield)  
  6.         print "Got %s" % n  
  7. #用例  
  8. r = receiver()  
  9. r.send("Hello world")    # 注意:无需初始调用.next()方法  

    协程的运行一般是无限期的,除非它被显式关闭或者自己退出。使用方法close()可以关闭输入值的流,例如:

[python] view plain copy
 
  1. >>>r.close()  
  2. >>>r.send(4)  
  3. Traceback (most recent call last):  
  4.    File <stdin>, line 1in <module>  
  5. StopIteration  

    关闭后,如果继续给协程发送值,就会引发StopIteration异常。正如前面关于生成器的内容中讲到的那样,close()操作将在协程内部引发GeneratorExit异常,例如:

[python] view plain copy
 
  1. def receiver():  
  2.     print "Ready to receive"  
  3.     try:  
  4.         while True:  
  5.             n = (yield)  
  6.             print "Got %s" % n  
  7.     except GeneratorExit:  
  8.         print "Receiver done"  

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

[python] view plain copy
 
  1. >>> r.throw(RuntimeError, "You're hosted!")  
  2. Traceback (most recent call last):  
  3.     File <stdin>, line 1in <module>  
  4.     File <stdin>, line 4in receiver  
  5. RuntimeError:You're hosted!  

    以这种方式引发的异常在协程中当前执行的yield语句处出现。协程可以选择捕捉异常并以正确方式处理它们。使用throw()方法作为给协程的异步信号并不安全——应该禁止从单独的执行线程或信号处理程序调用这个方法。
    如果yield表达式中提供了值,协程可以使用yield语句同时接收和发出返回值,例如:

[python] view plain copy
 
  1. def line_splitter(delimiter=None):  
  2.     print("Ready to split")  
  3.     result = None  
  4.     while True:  
  5.         line = (yield result)  
  6.         result = line.split(delimiter)  

    在这个例子中,我们使用协程的方式与前面相同。但是,现在调用send()方法也会生成一个结果,例如:

[python] view plain copy
 
  1. >>> s = line_splitter(",")  
  2. >>> s.next()  
  3. Ready to split  
  4. >>> s.send("A,B,C")  
  5. ['A''B''C']  
  6. >>> s.send("100,200,300")  
  7. ['100','200','300']  
  8. >>>  

    理解这个例子中的先后顺序至关重要。首个next()调用让协程向前执行到(yield result),这将返回result的初始值None。在接下来的send()调用中,接收到的值被放在line中并拆分到result中。send()方法的返回值就是传递给下一条yield语句的值。换句话说,send()方法的返回值来自下一个yield表达式,而不是接收send()传递的值的yield表达式。
    如果协程返回值,对使用throw()引发的异常进行处理时需要当心。如果使用throw()在协程中引发一个异常,传递给协程中下一条yield语句的值将作为throw()方法的结果返回。如果需要这个值却又忘记保存它,它就会消失不见。

来自  http://blog.csdn.net/liguo9860/article/details/6906182

 

普通分类: