2.1.1.4 双向沟通
每个yield
语句将一个值传递给调用者。这是由PEP 255(在Python2.2中实现)引入生成器简介的原因。但是,相反方向的沟通也是有用的。一个明显的方式可以是一些外部状态,全局变量或者是共享的可变对象。感谢PEP 342(在2.5中实现)使直接沟通成为可能。它通过将之前枯燥的yeild
语句转换为表达式来实现。当生成器在一个yeild
语句后恢复执行,调用者可以在生成器对象上调用一个方法,或者向生成器内部传递一个值,稍后由yield
语句返回,或者一个不同的方法向生成器注入一个异常。
第一个新方法是send(value),与next()类似,但是,向生成器传递值用于yield
表达式来使用。实际上,g.next()
和g.send(None)
是等价的。
第二个新方法是throw(type, value=None, traceback=None)等价于:
In [ ]:
raise type, value, traceback
在yield
语句的点上。
与raise不同 (在当前执行的点立即抛出异常), throw()
只是首先暂停生成器,然后抛出异常。挑选throw这个词是因为它让人联想到将异常放在不同的位置,这与其他语言中的异常相似。
当异常在生成器内部抛出时发生了什么?它可以是显性抛出或者当执行一些语句时,或者它可以注入在yield
语句的点上,通过throw()
方法的意思。在任何情况下,这些异常用一种标准方式传播:它可以被except
或finally
语句监听,或者在其他情况下,它引起生成器函数的执行中止,并且传播给调用者。
为了完整起见,应该提一下生成器迭代器也有close()函数,可以用来强制一个可能在其他情况下提供更多的值的生成器立即结束。它允许生成器del函数去销毁保持生成器状态的对象。
让我们定义一个生成器,打印通过send和throw传递的内容。
In [2]:
import itertools
def g():
print '--start--'
for i in itertools.count():
print '--yielding %i--' % i
try:
ans = yield i
except GeneratorExit:
print '--closing--'
raise
except Exception as e:
print '--yield raised %r--' % e
else:
print '--yield returned %s--' % ans
In [3]:
it = g()
next(it)
--start--
--yielding 0--
Out[3]:
0
In [4]:
it.send(11)
--yield returned 11--
--yielding 1--
Out[4]:
1
In [5]:
it.throw(IndexError)
--yield raised IndexError()--
--yielding 2--
Out[5]:
2
In [6]:
it.close()
--closing--
next 还是 next?
在Python2.X中,迭代器用于取回下一个值的方法是调用next。它通过全局方法next来唤醒,这意味着它应该调用next。就像全局函数iter调用iter。在Python 3.X中修正了这种前后矛盾,it.next变成it.next。对于其他的生成器方法 - send
和throw
-情况更加复杂,因为解释器并不隐性的调用它们。尽管如此,人们提出一种语法扩展,以便允许continue
接收一个参数,用于传递给循环的迭代器的send。如果这个语法扩展被接受,那么可能gen.send
将变成gen.send
。最后一个生成器函数,close非常明显是命名错误,因为,它已经隐性被唤起。