Python生成器定义与简单用法实例分析


Posted in Python onApril 30, 2018

本文实例讲述了Python生成器定义与简单用法。分享给大家供大家参考,具体如下:

一、什么是生成器

在Python中,由于受到内存的限制,列表容量肯定是有限的。例如我们创建一个包含一亿个元素的列表,Python首先会在内存中开辟足够的空间来存储这个包含一亿个元素的列表,然后才允许用户去使用这个列表,这就可能会导致以下问题:

1、内存中没有足够的内存空间开存储这个列表,从而导致列表无法创建

2、即使列表成功创建,然而仍会消耗很长的时间,导致程序效率低下

3、若用户只想访问列表前面的几个元素,则后面列表绝大多数元素占用的空间就都白白浪费了

为了有效解决以上的问题,Python中引入了一种“一边循环,一边计算”的新机制,即当用户需要使用某个对象时,Python才根据事先设计好的规则开辟内存空间创建这个对象供用户使用,而不是像列表一样事先将所有的对象都创建完毕之后再提供给用户使用。这种机制在Python中成为生成器(generator)。

二、生成器的创建

A、生成器推到式

与列表推到式类似,只不过生成器推导式使用()而非[],并且最终返回的是生成器而非列表

g=((i+2)**2 for i in range(2,30)) #g是一个生成器
print(g) #g为空,里面包含任何元素

运行结果:

<generator object <genexpr> at 0x0000000002263150>

B、yield关键字

在一个函数定义中包含yield关键字,则这个函数就不再是一个普通的函数,而是一个生成器(generator)

[说明]:yield指令可以暂停一个函数并返回其中间结果,使用该指令的函数将保存执行环境,并在必要时恢复

def fib(max):
  n,a,b=0,0,1
  while n<max:
    #print(b)
    yield b
    a,b=b,a+b
    n+=1
  return 'done'
f=fib(6)
print(f)

运行结果:

<generator object fib at 0x0000000002553150>

[注]:普通函数和变成生成器的函数的不同:

普通函数是顺序执行的,遇到return或是最后一行函数语句就返回。而变成生成器的函数在每次调用__next__()方法时执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行

f=fib(6)
print(f)
print(f.__next__())
print(f.__next__())
print('暂停一下')
print(f.__next__())
print(f.__next__())

运行结果:

<generator object fib at 0x00000000025631A8>
1
1
暂停一下
2
3

三、生成器方法(参考:伯乐在线)

1.close()方法:手动关闭生成器函数,后面的调用会直接返回StopIteration异常

def func():
  yield 1
  yield 2
  yield 3
g=func()
g.__next__()
g.close() #手动关闭生成器
g.__next__() #关闭后,yield 2和yield 3语句将不再起作用

运行结果:

Traceback (most recent call last):
  File "E:\py3Demo\Hello\generatorDemo.py", line 9, in <module>
    g.__next__() #关闭后,yield 2和yield 3语句将不再起作用
StopIteration

2.__next__()方法:返回生成器的下一次调用

def func():
  n=1
  for i in range(3):
    yield n
    n+=1
c=func()
a1=c.__next__()
a2=c.__next__()
a3=c.__next__()

[流程解释]:

对于普通的生成器,第一个__next__()方法的调用相当于启动生成器,此时会从生成器函数的第一行开始执行,直到第一次执行完yield语句(第四行)后,跳出生成器函数。

当调用第二个__next__()方法后,会重新进入生成器函数,并从yield语句的下一条语句(第五行)开始执行,直到重新运行到yield语句,执行后再次跳出生成器函数。

后面的__next__()方法调用以此类推

3.send()方法:接受外部传入的一个变量,并根据变量内容计算结果返回到生成器函数中

[注]:

(1)send()方法和__next__()方法相似,区别在于send()方法可以传递给yield表达式值,而__next__()方法不能传递特定的值,只能传递None给yield表达式,因此可以将generator.__next__()理解为generator.send(None)

(2)第一次调用生成器函数时,必须使用__next__()语句或是send(None),不能使用send发送一个非None的值给生成器函数,否则会出错,因为没有yield语句来接收这个值

def gen():
  value=0
  while True:
    receive=yield value
    if receive=='end':
      break
    value='Got:%s' %receive
g=gen()
print(g.__next__()) #或是print(g.send(None)),从而启动生成器
print(g.send('aaa'))
print(g.send(3))
print(g.send('end'))

运行结果:

0
Got:aaa
Got:3
Traceback (most recent call last):
  File "E:\py3Demo\Hello\generatorDemo.py", line 13, in <module>
    print(g.send('end'))
StopIteration

[流程解释]:

a.通过g.send(None)或g.__next__()启动生成器函数,并执行到第一个yield语句结束的位置并将函数挂起。此时执行完了yield语句,但是没有给receive赋值,因此yield value会输出value的初始值0

b.g.send('aaa')先将字符串‘aaa'传入到生成器函数中并赋值给receive,然后从yield语句的下一句重新开始执行函数(第五句),计算出value的值后返回到while头部开始新一轮的循环,执行到yield value语句时停止,此时yield value会输出‘Got:aaa',然后挂起

c.g.send(3)重复步骤b,最后输出结果为‘Got:3'

d.g.send('end')会使程序执行break然后跳出循环,从而函数执行完毕,得到StopIteration异常

4.throw()方法:向生成器发送一个异常。

def gen():
  while True:
    try:
      yield 'normal value' #返回中间结果,此处的yield和return的功能相似
      yield 'normal value2'
      print('I am here')
    except ValueError:
      print('We got ValueError')
    except Exception:
      print('Other errors')
      break
g=gen()
print(g.__next__())
print(g.throw(ValueError))
print(g.__next__())
print(g.throw(TypeError))

运行结果:

Traceback (most recent call last):
  File "E:\py3Demo\Hello\generatorDemo.py", line 17, in <module>
    print(g.throw(TypeError))
StopIteration
normal value
We got ValueError
normal value
normal value2
Other errors

[解释]:

a.print(g.__next__())会输出normal value,并停在yield 'normal value2'之前

b.由于执行了g.throw(ValueError),所以回跳过后续的try语句,即yield ‘normal value2'不会执行,然后进入到except语句,打印出‘We got ValueError'。之后再次进入到while语句部分,消耗一个yield,输出normal value

c.print(g.__next__())会执行yield ‘normal value2'语句,并停留在执行完该语句后的位置

d.g.throw(TypeError)会跳出try语句,因此print('I am here')不会被执行,然后打印‘Other errors',并执行break语句跳出while循环,然后到达程序结尾,打印StopIteration异常的信息

四、生成器的运用

import time
def consumer(name):
  print('%s准备吃包子啦!' %name)
  while True:
    baozi=yield #接收send传的值,并将值赋值给变量baozi
    print('包子[%s]来了,被[%s]吃了!' %(baozi,name))
def producer(name):
  c1=consumer('A') #把函数变成一个生成器
  c2=consumer('B')
  c1.__next__()#调用这个方法会走到yield处暂时返回
  c2.__next__()
  print('开始准备做包子啦!')
  for i in range(10):
    time.sleep(1)
    print('做了一个包子,分成两半')
    c1.send(i)
    c2.send(i)
producer('Tomwenxing')

运行结果:

A准备吃包子啦!
B准备吃包子啦!
开始准备做包子啦!
做了一个包子,分成两半
包子[0]来了,被[A]吃了!
包子[0]来了,被[B]吃了!
做了一个包子,分成两半
包子[1]来了,被[A]吃了!
包子[1]来了,被[B]吃了!
做了一个包子,分成两半
包子[2]来了,被[A]吃了!
包子[2]来了,被[B]吃了!
做了一个包子,分成两半
包子[3]来了,被[A]吃了!
包子[3]来了,被[B]吃了!
做了一个包子,分成两半
包子[4]来了,被[A]吃了!
包子[4]来了,被[B]吃了!
做了一个包子,分成两半
包子[5]来了,被[A]吃了!
包子[5]来了,被[B]吃了!
做了一个包子,分成两半
包子[6]来了,被[A]吃了!
包子[6]来了,被[B]吃了!
做了一个包子,分成两半
包子[7]来了,被[A]吃了!
包子[7]来了,被[B]吃了!
做了一个包子,分成两半
包子[8]来了,被[A]吃了!
包子[8]来了,被[B]吃了!
做了一个包子,分成两半
包子[9]来了,被[A]吃了!
包子[9]来了,被[B]吃了!

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

Python 相关文章推荐
Python语言编写电脑时间自动同步小工具
Mar 08 Python
在Python下使用Txt2Html实现网页过滤代理的教程
Apr 11 Python
用python编写第一个IDA插件的实例
May 29 Python
python中使用ctypes调用so传参设置遇到的问题及解决方法
Jun 19 Python
python字符串替换第一个字符串的方法
Jun 26 Python
python 直接赋值和copy的区别详解
Aug 07 Python
利用Pytorch实现简单的线性回归算法
Jan 15 Python
python函数调用,循环,列表复制实例
May 03 Python
win10下python3.8的PIL库安装过程
Jun 08 Python
升级keras解决load_weights()中的未定义skip_mismatch关键字问题
Jun 12 Python
Pycharm连接gitlab实现过程图解
Sep 01 Python
Python字符串的15个基本操作(小结)
Feb 03 Python
Python迭代器定义与简单用法分析
Apr 30 #Python
python 实现在txt指定行追加文本的方法
Apr 29 #Python
Python 实现在文件中的每一行添加一个逗号
Apr 29 #Python
python 把文件中的每一行以数组的元素放入数组中的方法
Apr 29 #Python
详谈python3 numpy-loadtxt的编码问题
Apr 29 #Python
python3.4.3下逐行读入txt文本并去重的方法
Apr 29 #Python
Python使用re模块实现信息筛选的方法
Apr 29 #Python
You might like
繁体中文转换为简体中文的PHP函数
2006/10/09 PHP
如何使用PHP往windows中添加用户
2006/12/06 PHP
CodeIgniter框架中_remap()使用方法2例
2014/03/10 PHP
php初始化对象和析构函数的简单实例
2014/03/11 PHP
php从memcache读取数据再批量写入mysql的方法
2014/12/29 PHP
PHPExcel简单读取excel文件示例
2016/05/26 PHP
PHP关于foreach复制知识点总结
2019/01/28 PHP
Aster vs Newbee BO5 第一场2.19
2021/03/10 DOTA
基于jquery跨浏览器显示的file上传控件
2011/10/24 Javascript
一个不错的仿携程自定义数据下拉选择select
2014/09/01 Javascript
JS+CSS模拟可以无刷新显示内容的留言板实例
2015/03/03 Javascript
PHPMyAdmin导入时提示文件大小超出PHP限制的解决方法
2015/03/30 Javascript
jquery使用each方法遍历json格式数据实例
2015/05/18 Javascript
js实现简洁的滑动门菜单(选项卡)效果代码
2015/09/04 Javascript
js实现显示手机号码效果
2017/03/09 Javascript
Vue原理剖析 实现双向绑定MVVM
2017/05/03 Javascript
关于bootstrap日期转化,bootstrap-editable的简单使用,bootstrap-fileinput的使用详解
2017/05/12 Javascript
EasyUI框架 使用Ajax提交注册信息的实现代码
2017/09/27 Javascript
微信小程序外卖选购页实现切换分类与数量加减功能案例
2019/01/15 Javascript
微信小程序简单的canvas裁剪图片功能详解
2019/07/12 Javascript
vue pages 多入口项目 + chainWebpack 全局引用缩写说明
2020/09/21 Javascript
[49:41]NB vs NAVI Supermajor小组赛A组 BO3 第一场 6.2
2018/06/03 DOTA
Python发送以整个文件夹的内容为附件的邮件的教程
2015/05/06 Python
解决Pycharm中import时无法识别自己写的程序方法
2018/05/18 Python
解决python给列表里添加字典时被最后一个覆盖的问题
2019/01/21 Python
python多线程实现同时执行两个while循环的操作
2020/05/02 Python
详解Pycharm与anaconda安装配置指南
2020/08/25 Python
python中@property的作用和getter setter的解释
2020/12/22 Python
如何用用Python将地址标记在地图上
2021/02/07 Python
CSS的background属性及CSS3的背景图片设置总结
2016/06/13 HTML / CSS
HTML5拖拽API经典实例详解
2018/04/20 HTML / CSS
园林资料员岗位职责
2013/12/30 职场文书
我的中国梦演讲稿600字
2014/08/19 职场文书
老生常谈 使用 CSS 实现三角形的技巧(多种方法)
2021/04/13 HTML / CSS
RPM包方式安装Oracle21c的方法详解
2021/08/23 Oracle
Nginx跨域问题解析与解决
2022/08/05 Servers