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深入学习之特殊方法与多范式
Aug 31 Python
Python验证码识别处理实例
Dec 28 Python
轻松掌握python设计模式之访问者模式
Nov 18 Python
使用python实现ANN
Dec 20 Python
python的numpy模块安装不成功简单解决方法总结
Dec 23 Python
Django读取Mysql数据并显示在前端的实例
May 27 Python
python实现多张图片拼接成大图
Jan 15 Python
pandas读取CSV文件时查看修改各列的数据类型格式
Jul 07 Python
pyinstaller还原python代码过程图解
Jan 08 Python
Python3读写Excel文件(使用xlrd,xlsxwriter,openpyxl3种方式读写实例与优劣)
Feb 13 Python
tensorflow安装成功import tensorflow 出现问题
Apr 16 Python
python如何查找列表中元素的位置
May 30 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获取URL中带#号等特殊符号参数的解决方法
2014/09/02 PHP
php源码分析之DZX1.5随机数函数random用法
2015/06/17 PHP
php获取flash尺寸详细数据的方法
2016/11/12 PHP
关于PHP定时发送服务的解决办法
2017/04/23 PHP
PHP常用操作类之通信数据封装类的实现
2017/07/16 PHP
php 比较获取两个数组相同和不同元素的例子(交集和差集)
2019/10/18 PHP
Laravel jwt 多表(多用户端)验证隔离的实现
2019/12/18 PHP
jQuery AJAX实现调用页面后台方法和web服务定义的方法分享
2012/03/01 Javascript
js iframe跨域访问(同主域/非同主域)分别深入介绍
2013/01/24 Javascript
js获取class的所有元素
2013/03/28 Javascript
js设置document.domain实现跨域的注意点分析
2015/05/21 Javascript
BootStrap的table表头固定tbody滚动的实例代码
2016/08/24 Javascript
从对象列表中获取一个对象的方法,依据关键字和值
2017/09/20 Javascript
(模仿京东用户注册)用JQuery实现简单表单验证,初学者必看
2018/01/08 jQuery
vue2.0 资源文件assets和static的区别详解
2018/04/08 Javascript
使用webpack编译es6代码的方法步骤
2019/04/28 Javascript
JS合并两个数组的3种方法详解
2019/10/24 Javascript
原生javascript实现类似vue的数据绑定功能示例【观察者模式】
2020/02/24 Javascript
vue 子组件watch监听不到prop的解决
2020/08/09 Javascript
js实现随机圆与矩形功能
2020/10/29 Javascript
[52:03]DOTA2-DPC中国联赛 正赛 Ehome vs iG BO3 第三场 1月31日
2021/03/11 DOTA
Python实现接受任意个数参数的函数方法
2018/04/21 Python
在python中安装basemap的教程
2018/09/20 Python
Python实现的对一个数进行因式分解操作示例
2019/06/27 Python
Python操作Jira库常用方法解析
2020/04/10 Python
python爬虫实现爬取同一个网站的多页数据的实例讲解
2021/01/18 Python
英国家居用品和床上用品零售商:P&B Home
2020/01/16 全球购物
大学生职业生涯规划范文
2014/01/22 职场文书
正风肃纪剖析材料
2014/02/18 职场文书
高中班主任评语大全
2014/04/25 职场文书
小学优秀班集体申报材料
2014/05/25 职场文书
介绍信如何写
2015/01/31 职场文书
《失物招领》教学反思
2016/02/20 职场文书
Eclipse+Java+Swing+Mysql实现电影购票系统(详细代码)
2022/01/18 Java/Android
python opencv将多个图放在一个窗口的实例详解
2022/02/28 Python
微信小程序APP的生命周期及页面的生命周期
2022/04/19 Javascript