python生成器用法实例详解


Posted in Python onNovember 22, 2019

本文实例讲述了python生成器用法。分享给大家供大家参考,具体如下:

1. 生成器

利用迭代器,我们可以在每次迭代获取数据(通过next()方法)时按照特定的规律进行生成。但是我们在实现一个迭代器时,关于当前迭代到的状态需要我们自己记录,进而才能根据当前状态生成下一个数据。为了达到记录当前状态,并配合next()函数进行迭代使用,我们可以采用更简便的语法,即生成器(generator)。生成器是一类特殊的迭代器。

2. 创建生成器方法1

要创建一个生成器,有很多种方法。第一种方法很简单,只要把一个列表生成式的 [ ] 改成 ( )

In [15]: L = [ x*2 for x in range(5)]
In [16]: L
Out[16]: [0, 2, 4, 6, 8]
In [17]: G = ( x*2 for x in range(5))
In [18]: G
Out[18]: <generator object <genexpr> at 0x7f626c132db0>
In [19]:

创建 L 和 G 的区别仅在于最外层的 [ ] 和 ( ) , L 是一个列表,而 G 是一个生成器。我们可以直接打印出列表L的每一个元素,而对于生成器G,我们可以按照迭代器的使用方法来使用,即可以通过next()函数、for循环、list()等方法使用。

In [19]: next(G)
Out[19]: 0
In [20]: next(G)
Out[20]: 2
In [21]: next(G)
Out[21]: 4
In [22]: next(G)
Out[22]: 6
In [23]: next(G)
Out[23]: 8
In [24]: next(G)
---------------------------------------------------------------------------
StopIteration               Traceback (most recent call last)
<ipython-input-24-380e167d6934> in <module>()
----> 1 next(G)
StopIteration:
In [25]:
In [26]: G = ( x*2 for x in range(5))
In [27]: for x in G:
  ....:   print(x)
  ....:   
0
2
4
6
8
In [28]:

3. 创建生成器方法2

generator非常强大。如果推算的算法比较复杂,用类似列表生成式的 for 循环无法实现的时候,还可以用函数来实现。

我们仍然用上一节提到的斐波那契数列来举例,回想我们在上一节用迭代器的实现方式:

class FibIterator(object):
  """斐波那契数列迭代器"""
  def __init__(self, n):
    """
    :param n: int, 指明生成数列的前n个数
    """
    self.n = n
    # current用来保存当前生成到数列中的第几个数了
    self.current = 0
    # num1用来保存前前一个数,初始值为数列中的第一个数0
    self.num1 = 0
    # num2用来保存前一个数,初始值为数列中的第二个数1
    self.num2 = 1
  def __next__(self):
    """被next()函数调用来获取下一个数"""
    if self.current < self.n:
      num = self.num1
      self.num1, self.num2 = self.num2, self.num1+self.num2
      self.current += 1
      return num
    else:
      raise StopIteration
  def __iter__(self):
    """迭代器的__iter__返回自身即可"""
    return self

注意,在用迭代器实现的方式中,我们要借助几个变量(n、current、num1、num2)来保存迭代的状态。现在我们用生成器来实现一下。

In [30]: def fib(n):
  ....:   current = 0
  ....:   num1, num2 = 0, 1
  ....:   while current < n:
  ....:     num = num1
  ....:     num1, num2 = num2, num1+num2
  ....:     current += 1
  ....:     yield num
  ....:   return 'done'
  ....:
In [31]: F = fib(5)
In [32]: next(F)
Out[32]: 1
In [33]: next(F)
Out[33]: 1
In [34]: next(F)
Out[34]: 2
In [35]: next(F)
Out[35]: 3
In [36]: next(F)
Out[36]: 5
In [37]: next(F)
---------------------------------------------------------------------------
StopIteration               Traceback (most recent call last)
<ipython-input-37-8c2b02b4361a> in <module>()
----> 1 next(F)
StopIteration: done

在使用生成器实现的方式中,我们将原本在迭代器__next__方法中实现的基本逻辑放到一个函数中来实现,但是将每次迭代返回数值的return换成了yield,此时新定义的函数便不再是函数,而是一个生成器了。简单来说:只要在def中有yield关键字的 就称为 生成器

此时按照调用函数的方式( 案例中为F = fib(5) )使用生成器就不再是执行函数体了,而是会返回一个生成器对象( 案例中为F ),然后就可以按照使用迭代器的方式来使用生成器了

In [38]: for n in fib(5):
  ....:   print(n)
  ....:   
1
1
2
3
5
In [39]:

但是用for循环调用generator时,发现拿不到generator的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中:

In [39]: g = fib(5)
In [40]: while True:
  ....:   try:
  ....:     x = next(g)
  ....:     print("value:%d"%x)   
  ....:   except StopIteration as e:
  ....:     print("生成器返回值:%s"%e.value)
  ....:     break
  ....:   
value:1
value:1
value:2
value:3
value:5
生成器返回值:done
In [41]:

总结

使用了yield关键字的函数不再是函数,而是生成器。(使用了yield的函数就是生成器)

yield关键字有两点作用:

保存当前运行状态(断点),然后暂停执行,即将生成器(函数)挂起
将yield关键字后面表达式的值作为返回值返回,此时可以理解为起到了return的作用
可以使用next()函数让生成器从断点处继续执行,即唤醒生成器(函数)
Python3中的生成器可以使用return返回最终运行的返回值,而Python2中的生成器不允许使用return返回一个返回值(即可以使用return从生成器中退出,但return后不能有任何表达式)。

4. 使用send唤醒

我们除了可以使用next()函数来唤醒生成器继续执行外,还可以使用send()函数来唤醒执行。使用send()函数的一个好处是可以在唤醒的同时向断点处传入一个附加数据。

例子:执行到yield时,gen函数作用暂时保存,返回i的值; temp接收下次c.send(“python”),send发送过来的值,c.next()等价c.send(None)

In [10]: def gen():
  ....:   i = 0
  ....:   while i<5:
  ....:     temp = yield i
  ....:     print(temp)
  ....:     i+=1
  ....:

使用send

n [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
In [48]:

用next函数

In [11]: f = gen()
In [12]: next(f)
Out[12]: 0
In [13]: next(f)
None
Out[13]: 1
In [14]: next(f)
None
Out[14]: 2
In [15]: next(f)
None
Out[15]: 3
In [16]: next(f)
None
Out[16]: 4
In [17]: next(f)
None
---------------------------------------------------------------------------
StopIteration               Traceback (most recent call last)
<ipython-input-17-468f0afdf1b9> in <module>()
----> 1 next(f)
StopIteration:

使用__next__()方法(不常使用)

In [18]: f = gen()
In [19]: f.__next__()
Out[19]: 0
In [20]: f.__next__()
None
Out[20]: 1
In [21]: f.__next__()
None
Out[21]: 2
In [22]: f.__next__()
None
Out[22]: 3
In [23]: f.__next__()
None
Out[23]: 4
In [24]: f.__next__()
None
---------------------------------------------------------------------------
StopIteration               Traceback (most recent call last)
<ipython-input-24-39ec527346a9> in <module>()
----> 1 f.__next__()
StopIteration:

希望本文所述对大家Python程序设计有所帮助。

Python 相关文章推荐
Python使用scrapy采集数据过程中放回下载过大页面的方法
Apr 08 Python
Python使用迭代器捕获Generator返回值的方法
Apr 05 Python
Python连接phoenix的方法示例
Sep 29 Python
python多进程中的内存复制(实例讲解)
Jan 05 Python
Python内置random模块生成随机数的方法
May 31 Python
python循环定时中断执行某一段程序的实例
Jun 29 Python
解决pycharm 工具栏Tool中找不到Run manager.py Task的问题
Jul 01 Python
django 使用 PIL 压缩图片的例子
Aug 16 Python
python实现四人制扑克牌游戏
Apr 22 Python
pytorch中的numel函数用法说明
May 13 Python
Python list列表删除元素的4种方法
Nov 01 Python
通过Python把学姐照片做成拼图游戏
Feb 15 Python
关于pandas的离散化,面元划分详解
Nov 22 #Python
Python协程 yield与协程greenlet简单用法示例
Nov 22 #Python
使用pandas实现连续数据的离散化处理方式(分箱操作)
Nov 22 #Python
在OpenCV里使用Camshift算法的实现
Nov 22 #Python
利用Python的sympy包求解一元三次方程示例
Nov 22 #Python
Python matplotlib以日期为x轴作图代码实例
Nov 22 #Python
python快速排序的实现及运行时间比较
Nov 22 #Python
You might like
解析php多线程下载远程多个文件
2013/06/25 PHP
php网站地图生成类示例
2014/01/13 PHP
ThinkPHP之用户注册登录留言完整实例
2014/07/22 PHP
php解析字符串里所有URL地址的方法
2015/04/03 PHP
PHP压缩图片功能的介绍
2019/03/21 PHP
简单实用jquery版三级联动select示例
2013/07/04 Javascript
JS短路原理的应用示例 精简代码的途径
2013/12/13 Javascript
jQuery插件实现控制网页元素动态居中显示
2015/03/24 Javascript
JavaScript Function函数类型介绍
2015/04/08 Javascript
jquery自定义插件开发之window的实现过程
2016/05/06 Javascript
Node.js环境下编写爬虫爬取维基百科内容的实例分享
2016/06/12 Javascript
javascript 中的事件委托详解
2016/10/25 Javascript
JS实现unicode和UTF-8之间的互相转换互转
2017/07/05 Javascript
基于Koa2写个脚手架模拟接口服务的方法
2018/11/27 Javascript
使用eslint和githooks统一前端风格的技巧
2020/07/29 Javascript
[33:23]VG vs Pain 2018国际邀请赛小组赛BO2 第二场 8.18
2018/08/19 DOTA
Python实现去除代码前行号的方法
2015/03/10 Python
在python环境下运用kafka对数据进行实时传输的方法
2018/12/27 Python
python 环境搭建 及python-3.4.4的下载和安装过程
2019/07/20 Python
Pytorch中的variable, tensor与numpy相互转化的方法
2019/10/10 Python
Python实现自定义读写分离代码实例
2019/11/16 Python
Python实现图像去噪方式(中值去噪和均值去噪)
2019/12/18 Python
matplotlib 对坐标的控制,加图例注释的操作
2020/04/17 Python
基于python实现把json数据转换成Excel表格
2020/05/07 Python
Spring @Enable模块驱动原理及使用实例
2020/06/23 Python
Python如何获取文件路径/目录
2020/09/22 Python
大学生实习期自我评价范文
2013/10/03 职场文书
小学红领巾中秋节广播稿
2014/01/13 职场文书
趣味体育活动方案
2014/02/08 职场文书
个人自荐材料
2014/05/23 职场文书
医院消毒隔离制度
2015/08/05 职场文书
爱国之歌(8首)
2019/09/29 职场文书
MySQL令人咋舌的隐式转换
2021/04/05 MySQL
Html5生成验证码的示例代码
2021/05/10 Javascript
HTML中的表单Form实现居中效果
2021/05/25 HTML / CSS
5种 JavaScript 方式实现数组扁平化
2021/10/05 Javascript