Python 生成器与它的 send,throw,close 方法
转载请注明出处:https://blog.csdn.net/jpch89/article/details/87036970
在生成器中,无论生成器是什么状态,都可以直接使用throw与close。
生成器这一块,对于next,send网上的介绍比较多,但对于throw以及close很多书上写的比较少,可能用的比较少,好在网上有很多介绍。
以下是流畅的Python对throw和close的介绍:
generator.throw(exc_type[, exc_value[, traceback]])
致使生成器在暂停的yield表达式处抛出指定的异常。如果生成器处理了抛出的异常,代码会向前执行到下一个yield表达式,而产出的值会调用generator.throw方法得到的返回值。如果生成器没有处理抛出的异常,异常会向上冒泡,传到调用方的上下文中。
generator.close()
致使生成器在暂停的yield表达式处抛出GeneratorExit异常。如果生成器没有处理这个异常,或者抛出了StopIteration异常(通常是指运行到结尾),调用方不会报错。如果收到GeneratorExit异常,生成器一定不能产出值,否则解释器会抛出RuntimeError异常。生成器抛出的其他异常会向上冒泡,传给调用方。
next就是send(None)
生成器第一次需要预激,到达第一个yield处,预激可以用next或send(None),预激将产出第一个值,并到达第一个yield处
到达yield处可以send(object)了。
In [319]: def demo(): ...: for i in range(5): ...: res = yield i ...: print(res) ...: In [320]: d = demo() In [321]: type(d) Out[321]: generator In [322]: next(d) Out[322]: 0 In [323]: d.send('ok') ok Out[323]: 1 In [324]: d.send(None) None Out[324]: 2 In [325]: next(d) None Out[325]: 3 In [326]: next(d) None Out[326]: 4 In [327]: next(d) None --------------------------------------------------------------------------- StopIteration Traceback (most recent call last) <ipython-input-327-9b2daf1403f5> in <module> ----> 1 next(d) StopIteration: In [328]: next(d) --------------------------------------------------------------------------- StopIteration Traceback (most recent call last) <ipython-input-328-9b2daf1403f5> in <module> ----> 1 next(d) StopIteration:
简单的测试了next与send,接着测试throw.
按照前面书中的说明,throw以后如果抓取到错误,执行except内的语句,然后寻找下一个yield,所以如果在最后一个yield处throw,就算抓取在生成器中抓取到错误也会上浮错误信息
StopIteration。当throw进去一个错误,生成器内部没有处理,当外部调用生成器的时候捕获了上浮的错误,此时生成器已经关闭,如果再次使用next与send会包stopIteration。
(这里我重点笔记一下throw(StopIteration),因为当throw这个的时候,报的错误是RuntimeError)
In [1]: def xx(): ...: yield 1 ...: In [2]: x = xx() In [3]: x.throw(NameError) --------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-3-9c1a5a069c11> in <module> ----> 1 x.throw(NameError) <ipython-input-1-c25019d2c434> in xx() ----> 1 def xx(): 2 yield 1 3 NameError: In [4]: x = xx() In [5]: next(x) Out[5]: 1 In [6]: x.throw(NameError) --------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-6-9c1a5a069c11> in <module> ----> 1 x.throw(NameError) <ipython-input-1-c25019d2c434> in xx() 1 def xx(): ----> 2 yield 1 3 NameError: In [7]:
上面这个是普通的没有去获取任何异常的情况下,可以发现,生成器没有预激的情况下,也可以throw错误,只不过上浮的错误显示,报错的方位不一样。
没有预激的生成器在def处就发现了错误,预激的生成器在第一个yield处发生了错误。
我测试了很多不同的错误,一般不管在预激还是没有预激的情况下,扔什么错误,在没有捕获的情况下,就上浮错误,但StopIterations是一个例外。
In [33]: def xx(): ...: yield 1 ...: yield 2 ...: In [34]: x = xx() In [35]: next(x) Out[35]: 1 In [37]: try: ...: x.throw(ValueError,'ValueError_my') ...: except ValueError as e: ...: print(e) ...: ValueError_my In [38]: inspect.getgeneratorstate(x) Out[38]: 'GEN_CLOSED' In [39]: next(x) --------------------------------------------------------------------------- StopIteration Traceback (most recent call last) <ipython-input-39-92de4e9f6b1e> in <module> ----> 1 next(x) StopIteration:
上面是一个没有捕获错误,外部捕获了错误,但生成器已经关闭了。
In [40]: def xx(): ...: try: ...: yield 1 ...: yield 2 ...: except TypeError: ...: print('info type error') ...: ...: In [41]: x = xx() In [42]: x.throw(TypeError) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-42-c568e586b030> in <module> ----> 1 x.throw(TypeError) <ipython-input-40-97b8907fc7a9> in xx() ----> 1 def xx(): 2 try: 3 yield 1 4 yield 2 5 except TypeError: TypeError: In [43]: x = xx() In [44]: next(x) Out[44]: 1 In [45]: x.throw(TypeError) info type error --------------------------------------------------------------------------- StopIteration Traceback (most recent call last) <ipython-input-45-c568e586b030> in <module> ----> 1 x.throw(TypeError) StopIteration: In [46]: def xx(): ...: try: ...: yield 1 ...: yield 2 ...: except TypeError: ...: print('info type error') ...: yield 3 ...: ...: ...: In [47]: x = xx() In [48]: next(x) Out[48]: 1 In [49]: x.throw(TypeError) info type error Out[49]: 3 In [50]: next(x) --------------------------------------------------------------------------- StopIteration Traceback (most recent call last) <ipython-input-50-92de4e9f6b1e> in <module> ----> 1 next(x) StopIteration: In [51]:
上面的例子测试了没有预激的情况下,throw错误,生成器内部完全不能捕获任何没有预激情况下的错误,而且该生成器也将关闭。
在预激的情况下,可以捕获设置的错误,并且寻找下一个yield,如果没有下一个yield,上浮StopIteration
In [54]: def xx(): ...: try: ...: yield 1 ...: yield 2 ...: except StopIteration: ...: print('info stop') ...: yield 3 ...: ...: ...: In [55]: x = xx() In [56]: x.throw(StopIteration) --------------------------------------------------------------------------- StopIteration Traceback (most recent call last) <ipython-input-54-ebd45e0bac35> in xx() ----> 1 def xx(): 2 try: 3 yield 1 StopIteration: The above exception was the direct cause of the following exception: RuntimeError Traceback (most recent call last) <ipython-input-56-c41944ac436e> in <module> ----> 1 x.throw(StopIteration) RuntimeError: generator raised StopIteration In [57]: x = xx() In [58]: next(x) Out[58]: 1 In [59]: x.throw(StopIteration) info stop Out[59]: 3 In [60]: x.throw(StopIteration) --------------------------------------------------------------------------- StopIteration Traceback (most recent call last) <ipython-input-54-ebd45e0bac35> in xx() 6 print('info stop') ----> 7 yield 3 8 StopIteration: The above exception was the direct cause of the following exception: RuntimeError Traceback (most recent call last) <ipython-input-60-c41944ac436e> in <module> ----> 1 x.throw(StopIteration) RuntimeError: generator raised StopIteration In [61]:
从上面的列子可以看出只要没有捕获StopItoration,就上浮RuntimeError,而且这个错误是就因为StopItoration引起的。
但如果在生成器内部预设了捕获StopItoration,则还是跟不同的逻辑是一样的。
我的理解为,应该为如果没有捕获StopItoration,直接用了什么方法调用生成了新的错误,上浮给调用者,避免与StopItoration错误重复。
最后是close,按照书中的说法跟我自己的理解,就是在yield处抛出Generation,可以通过except捕获到错误,但捕获了以后,后面不能再有yield产出值,要不然包RuntimeError。
就算不捕获也没关系,不会上浮任何的错误,只不过该协程已经关闭了。
In [61]: def xx(): ...: try: ...: yield 1 ...: yield 2 ...: except GeneratorExit: ...: print('info stop') ...: yield 3 ...: ...: ...: ...: In [62]: x= xx() In [63]: x.close() In [64]: next(x) --------------------------------------------------------------------------- StopIteration Traceback (most recent call last) <ipython-input-64-92de4e9f6b1e> in <module> ----> 1 next(x) StopIteration: In [65]: x= xx() In [66]: next(x) Out[66]: 1 In [67]: x.close() info stop --------------------------------------------------------------------------- RuntimeError Traceback (most recent call last) <ipython-input-67-f6d031a59762> in <module> ----> 1 x.close() RuntimeError: generator ignored GeneratorExit
In [68]: x= xx()
In [69]: next(x)
Out[69]: 1
In [70]: x.throw(GeneratorExit)
info stop
Out[70]: 3
最后,我自己总结一下thorw与close的笔记,两个函数都可以不需要预激的情况下面执行。
但执行的时候,生成器内部不会捕获到该异常。
预激了以后,throw所有的异常都能捕获,捕获到该异常后,向下执行寻找下一个yield,产出值,没有yield就上浮StopItoration
close在预激了以后,能通过except捕获到该GeneratorExit异常,但except向下的代码不能出现yield产出值,要不然会抛出RuntimeError。
如果throw(StopItoration),如果没有捕获该错误,上浮的错误为RuntimeError。
一个生成器关闭了,还能继续使用close函数,且不会报错。
最后,一个生成器内部如果发生错误,没有捕获,这个生成器就马上进行关闭。