1. 生成器是什么?

利用迭代器,我们可以在每次迭代获取数据(通过next()方法)时按照特定的规律进行生成。但是我们在实现一个迭代器时,关于当前迭代到的状态需要我们自己记录,进而才能根据当前状态生成下一个数据。为了达到记录当前状态,并配合next()函数进行迭代使用,我们可以采用更简便的语法,即生成器(generator)。生成器是一类特殊的迭代器。
 
 
2.列表生成式
 
2.1列表推导式书写形式:  
[表达式 for 变量 in 列表]    或者  [表达式 for 变量 in 列表 if 条件]
示例代码如下:
>>>[i for i in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

示例代码如下:

>>>[x**2 for x in [1,2,5,8,7] if x>5]
[64, 49]
 
2.2 列表生成式:
列表生成式即List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的生成式。
列表生成式书写形式:  
 (表达式 for 变量 in 列表)   或者  (表达式 for 变量 in 列表 if 条件)
列表生成式不同于列表推导式, 创建列表推导式和列表生成式的区别仅在于最外层的”[]“和"()",列表推导式是一个list,而列表生成式是一个generator。切不可相混淆。
 

3. 创建生成器方法1

生成器表达式:要创建一个生成器,有很多种方法。第一种方法很简单,即生成器表达式,只要把一个列表推导式的 [ ] 改成 ( );
如下代码返回的不是一个列表,而是一个生成器对象, 可通过 next() 函数 或者__next__() 方法获得generator的下一个返回值:
>>>obj = (i for i in range(5))
>>>obj
<generator object <genexpr> at 0x03F79D80>
>>>next(obj)
0
>>>next(obj)
1
>>>obj.__next__()
2
>>>obj.__next__()
3
>>>obj.__next__()
4

注意:generator保存的是算法,每次调用next()方法,就计算出generator的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。因为generator也是可迭代对象,所以可以使用for循环来取出数据。

>>> list = (x *2 for x in range(5))
>>> for num in list:
... print(num)
...
0
2
4
6
8

4. 创建生成器方法2

生成器函数: 在函数中如果出现了yield关键字,那么该函数就不再是普通函数,而是生成器函数。

但是生成器函数可以生产一个无限的序列,这样列表根本没有办法进行处理。yield 的作用就是把一个函数变成一个 generator,带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个 generator。generator非常强大。如果推算的算法比较复杂,用类似列表生成式的 for 循环无法实现的时候,还可以用函数来实现。

In [30]: def fib(n):
  ....:   current = 0
  ....:   num1, num2 = 0, 1
  ....:   while current < n:
  ....:     num = num1
  ....:     nm1, num2 = num2, num1+num2
  ....:     current += 1
  ....:     yield num
  ....:     return 'done'
....:
In [31]: F = fib(5) In [32]: next(F)
Out[32]: 0 In [33]: next(F)
Out[33]: 1 In [34]: next(F)
Out[34]: 1 In [35]: next(F)
Out[35]: 2 In [36]: next(F)
Out[36]: 3 In [37]: next(F)
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-37-8c2b02b436bn> in <module>()
----> 1 next(F) StopIteration: done
 
在使用生成器实现的方式中,我们将原本在迭代器__next__方法中实现的基本逻辑放到一个函数中来实现,但是将每次迭代返回数值的return换成了yield,此时新定义的函数便不再是函数,而是一个生成器了。
简单来说:只要在def中有yield关键字的 就称为 生成器.
在 for 循环执行时,每次循环都会执行 fab 函数内部的代码,执行到 yield num 时,fab 函数就返回一个迭代值,下次迭代时,代码从 yield num 的下一条语句继续执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行,直到再次遇到 yield。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。

5. yield 与 return

5.1 在一个生成器中,如果没有return,则默认执行到函数完毕时返回StopIteration;

>>> def g1():
...   yield 6
...
>>> g=g1()
>>> next(g) #第一次调用next(g)时,会在执行完yield语句后挂起,所以此时程序并没有执行结束。
6
>>> next(g) #程序试图从yield语句的下一条语句开始执行,发现已经到了结尾,所以抛出StopIteration异常。
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>

5.2 如果在return后返回一个值,那么这个值为StopIteration异常的说明,不是程序的返回值。

>>> def g2():
...   yield 'hello'
...   return 'error information'
...
>>> g=g2()
>>> next(g)
'hello'
>>> next(g)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration: error information

5.3 如果遇到return,如果在执行过程中 return,则直接抛出 StopIteration 终止迭代。

>>> def g3():
...   yield 'a'
...   return
...   yield 'b'
...
>>> g=g3()
>>> next(g) #程序停留在执行完yield 'a'语句后的位置。
'a'
>>> next(g) #执行到此处,程序发现下一条语句是return,所以抛出StopIteration异常,这样yield 'b'语句永远也不会执行。
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration

5.4 close()函数   手动关闭生成器函数,后面的调用会直接返回StopIteration异常。

>>> def g4():
...  yield 1
...  yield 2
...  yield 3
...
>>> g=g4()
>>> next(g)
1
>>> g.close()
>>> next(g) #关闭后,yield2 和yield3 语句将不再起作用
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
 
5.5 send()函数 
我们除了可以使用next()函数来唤醒生成器继续执行外,还可以使用send()函数来唤醒执行。使用send()函数的一个好处是可以在唤醒的同时向断点处传入一个附加数据。生成器函数最大的特点是可以接受外部传入的一个变量,并根据变量内容计算结果后返回。
In [35]: def gen():
....:     i = 0
....:     while i<5:
....:       temp = yield i
....:       print(temp)
....:       i+=1
....: In [43]: f = gen() In [44]: next(f)
Out[44]: 0 In [45]: f.send('haha')
haha
Out[45]: 1 In [46]: next(f)
None
Out[46]: 2 In [47]: f.send('haha')
haha
Out[47]: 3

上方代码执行到yield时,gen函数作用暂时保存,返回 i 的值; temp接收下次f.send("haha"),即send发送过来的值,next(f)等价f.send(None)

总结

  • 使用了yield关键字的函数不再是函数,而是生成器。(使用了yield的函数就是生成器)
  • 可作用于for循环的对象都是Iterable类型;
  • next(f) 等价于 f.send(None)
  • yield关键字有两点作用:
    • 保存当前运行状态(断点),然后暂停执行,即将生成器(函数)挂起
    • 将yield关键字后面表达式的值作为返回值返回,此时可以理解为起到了return的作用
  • 可以使用next()函数让生成器从断点处继续执行,即唤醒生成器(函数)

生成器(generator) 详解的更多相关文章

  1. MyBatis Generator 详解

    MyBatis Generator中文文档 MyBatis Generator中文文档地址:http://mbg.cndocs.tk/ 该中文文档由于尽可能和原文内容一致,所以有些地方如果不熟悉,看中 ...

  2. MyBatis Generator 详解 【转来纯为备忘】

    版权声明:版权归博主所有,转载请带上本文链接!联系方式:abel533@gmail.com   目录(?)[+] MyBatis Generator中文文档 运行MyBatis Generator X ...

  3. MyBatis Generator 详解(转)

    MyBatis Generator中文文档 MyBatis Generator中文文档地址:http://mbg.cndocs.tk/ 该中文文档由于尽可能和原文内容一致,所以有些地方如果不熟悉,看中 ...

  4. MyBatis Generator 详解 专题

    idea中有plugin可提高效率: http://www.henryxi.com/use-idea-mybatis-plugin-generate-mapper-files eg: <?xml ...

  5. ES6新特性三: Generator(生成器)函数详解

    本文实例讲述了ES6新特性三: Generator(生成器)函数.分享给大家供大家参考,具体如下: 1. 简介 ① 理解:可以把它理解成一个函数的内部状态的遍历器,每调用一次,函数的内部状态发生一次改 ...

  6. python yield generator 详解

    本文将由浅入深详细介绍yield以及generator,包括以下内容:什么generator,生成generator的方法,generator的特点,generator基础及高级应用场景,genera ...

  7. 面向对象设计模式_生成器模式详解(Builder Pattern)

    首先提出一个很容易想到应用场景: 手机的生产过程:手机有非常多的子件(部件),成千上万,不同品牌的手机的生产过程都是复杂而有所区别的,相同品牌的手机在设计上也因客户需求多样化,大到型号,小到颜色,是否 ...

  8. generator详解

    generator函数 yield可以返回值,也可以传入值 形式: 注意!generator不能写成arrow function的形式!!! function *函数(){ 代码1... let a ...

  9. (转)Python中的generator详解

    本文转自:http://www.cnblogs.com/xybaby/p/6322376.html 作者:xybaby 注:本文在原文基础上做了一点点修改,仅仅作为个人理解与记忆,建议直接查看原文. ...

随机推荐

  1. LAG函数实现环比

    ,)OVER(ORDER BY 年月) 环比金额 from( 年, 季度, 年月 ,SUM(金额本位币) 金额 FROM ( SELECT * FROM [dbo].[T_output] ) cb_v ...

  2. beego项目和go项目 打包部署到linux

    参考文章: https://www.jianshu.com/p/64363dff9721 [beego项目] 一. 打包 1. 打开Terminal 定位到工程的 main.go 文件夹目录 2. 执 ...

  3. Collection集合常用的功能

    package demo06; import java.util.ArrayList;import java.util.Collection; /** java.util接口 Collection&l ...

  4. HTML5 & CSS初学者教程(详细、通俗易懂)

    前端语言基础:HTML5 & CSS (一) HTML5:超文本标记语言 (1) 基本概念 是由一系列成对出现的元素标签(标记)嵌套组合而成 ( XML也是标签构成的 ) 这些标签以的形式出现 ...

  5. storm drpc分布式本地和远程调用模式讲解

    一.drpc 的介绍 1.rpc RPC(Remote Procedure Call)—远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议. 2.drpc drp ...

  6. 正式发布! .NET开发控件集ComponentOne 新版本加入Blazor UI

    近期,由葡萄城推出的ComponentOne .NET开发控件集正式发布最新版本! ComponentOne 是一套专注于企业 .NET开发.支持 .NET Core 平台,并完美集成于 Visual ...

  7. Websocket基础梳理

    Websocket原理: websocket介绍: WebSocket(http://dev.w3.org/html5/websockets)是HTML5规范(http://www.w3.org/TR ...

  8. [转]mac升级Nodejs和Npm到最新版

    第一步,先查看本机node.js版本: node -v 第二步,清除node.js的cache: sudo npm cache clean -f 第三步,安装 n 工具,这个工具是专门用来管理node ...

  9. .Net Core 3.0原生Json解析器

    微软官方博客中描述了为什么构造了全新的Json解析器而不是继续使用行业准则Json.Net 微软博客地址:https://devblogs.microsoft.com/dotnet/try-the-n ...

  10. 【AC自动机】最短母串

    [题目链接] https://loj.ac/problem/10061 [题意] 给定 n 个字符串 S1-Sn,要求找到一个最短的字符串 T,使得这 n 个字符串都是 T 的子串. [题解] 类似于 ...