Python 生成器与它的 send,throw,close 方法(转帖以及记录)
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函数,且不会报错。
最后,一个生成器内部如果发生错误,没有捕获,这个生成器就马上进行关闭。
浙公网安备 33010602011771号