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函数,且不会报错。

最后,一个生成器内部如果发生错误,没有捕获,这个生成器就马上进行关闭。

posted @ 2020-01-22 22:15  就是想学习  阅读(632)  评论(0编辑  收藏  举报