理解Python中的生成器

系统 1126 0

Python生成器是什么?

先说一下生成器函数,抽象地说,生成器函数就是一个顺序执行过程的抽象。具体地说,它就是一种特殊的函数,这种特殊性源于这个函数中出现了一个yield关键字。解释器在发现函数中有yield关键字时,将这个函数标记为一个生成器函数,其执行的结果会返回一个生成器,而这个生成器是支持迭代器协议的。

创建一个生成器函数

生成器函数的创建是非常简单的:

            
              In [1]: def mygenerator(): 
   ...:     yield 1 
   ...:     
            
          

正如前面提到的,如果一个函数内部出现了yield关键字,那么这个函数就被Python解释器标记为生成器函数:

            
              In [2]: import inspect                                                                                    

In [3]: inspect.isgeneratorfunction(mygenerator)                                                          
Out[3]: True


            
          

我们这个生成器函数的逻辑很简单,它只生成一个东西,就是数字1,下面我们就执行这个生成器函数,获取对应的生成器,并访问这个数字1:

            
              In [4]: gen = mygenerator()                                                                               

In [5]: next(gen)                                                                                         
Out[5]: 1

            
          

这里发生了什么?首先我们通过执行mygenerator生成器函数获得了一个生成器gen,然后通过迭代器协议,对gen调用next函数,它就直接生成了1。这和我们的预期应该是一致的,这和我们在生成器函数中写的逻辑一致。那当我们再次对gen调用next函数会发生什么呢?

            
              In [6]: next(gen)                                                                                         
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)

              
                 in 
                
                  
----> 1 next(gen)

StopIteration: 
                
              
            
          

前面对gen调用next让生成器执行到了第一个yield表达式,并将yield后面的值(1)返回给我们,之后生成器暂停于yield这个表达式。当我们再次对gen调用next的时候,生成器从上一个yield表达式处返回,并且不会再生成任何值,毕竟生成器函数中只有1行代码。而根据迭代器协议,如果没有值可返回的话,这里直接触发了StopIteration异常,这里的这个异常,更确切地说应该是一种信号。

与生成器内部进行通信,影响生成器内部的行为

我们说yield表达式,既然是表达式,其必定有一个返回值,而我们在前面的简单生成器函数中,并没有用到yield表达式的返回值,那么,默认情况下,yield表达式会返回什么值呢?我们看一下:

            
              In [11]: def mygenerator(): 
    ...:     v = yield 1 
    ...:     print(v) 
    ...:                                                                                                  

In [12]: gen = mygenerator()                                                                              

In [13]: next(gen)                                                                                        
Out[13]: 1

In [14]: next(gen)                                                                                        
None
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)

              
                 in 
                
                  
----> 1 next(gen)

StopIteration: 

                
              
            
          

在第二次对gen调用next的时候,我们发现在抛出异常之前,打印出了None,也就是yield表达式的值。我们可以通过生成器对象的send方法来为上次yield表达式执行提供一个返回值:

            
              In [15]: gen = mygenerator()                                                                              

In [16]: next(gen)                                                                                        
Out[16]: 1

In [17]: gen.send(1000)                                                                                   
1000
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)

              
                 in 
                
                  
----> 1 gen.send(1000)

StopIteration: 

                
              
            
          

原来的None,变成了这里我们通过send方法传递的1000,我们成功影响了yield表达式的返回值!但是这里一定要注意一个问题,就是send方法调用的时候,要确保生成器对象至少调用了一次next之后,也就是要保证至少有一次yield表达式执行,这也很容易理解,因为如果yield表达式还没有执行,那这个表达式怎么会有值呢?

 


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论