生成器

1. 什么是生成器

大家知道通过列表生成式(不知道的可自行百度一下),我们可以直接创建一个列表,但是,受内存限制,列表内容肯定是有限的。比如我们要创建一个包含100万个元素的列表,这100万个元素会占用很大的内存空间,而且如果我们仅仅需要访问前面几个元素的话,那后面绝大多数的元素占用的空间就都白白浪费了。设想一下如果列表中的元素能够在循环使用的过程中推算出来,即用一个推算一个,这样是不是就不用一次性生成全部元素从而大大节省了内存空间呢。在python中这种一边循环一边计算的机制称为生成器:generator。

2. 创建生成器的方法

这里主要介绍2中创建生成器的方法:

方法一:这种方法最简单,只要把一个列表生成式的[]改成()即可,如下:

>>>L = [x*2 for x in range(10)]

>>>L

>>>[0,2,4,6,8,10,12,14,16,18]

>>>G = (x*2 for x in range(10))

>>>G

>>><generator object <genexpr> at 0x7f628d120kb0

创建L和G的区别仅在于最外层的[]和(),但结果是L是一个列表而G是一个生成器,我们知道列表我们可以直接打印出来,那么生成器的值我们应该怎么获取呢,同样有2中方式,第一是通过next()函数获得生成器的下一个返回值:

>>>next(G)

>>>0

>>>next(G)

>>>2

但是这样获取太过麻烦,如果像上面说的需要100万个元素时我们这样写岂不是会累死,而且如果到最后没有元素还会产生StopIteration的异常。

下面说一下获取生成器值的第二种方法:使用for循环,因为生成器也是可迭代的对象,我们创建一个生成器后基本是不会使用next函数调用的,而是通过for循环来迭代,并且不用关心StopIteration异常。

方法二:使用yield关键字创建生成器

方法一种我们介绍了最简单的生成方法,是把列表生成式的[]改为(),这种方式适合推算算法比较简单的场景。试想如果推算算法和复杂,用类似列表生成式一行代码无法实现的时候该怎么办呢?幸运的是generator非常强大,可以把一个函数作为一个生成器。比如著名的斐波那契数列,除第一和第二个数一样外,其它任意一个数都是由前两个数相加而得到:1,1,2,3,5,8...

这时列表生成式就无法满足了,但是我们可以用函数很容易的打印出来:

def fib(times):
n = 0
a,b = 0,1
while n < times:
print(b)
a,b = b,a+b
n+=1
return 'done'
#调用fib函数生成5个数
fib(5)
#输出结果如下:
1
1
2
3
5
done

可以看出,fib函数定义了斐波那契数列的推算规则,可以从第一个元素开始推算出后续任意的元素,这种逻辑非常类似生成器generator,也就是说上面的函数离生成器仅一步之遥。这里只需要把print(b)改为yield b就变成一个生成器了。

def fib(times):
n = 0
a,b = 0,1
while n < times:
yield b
a,b = b,a+b
n+=1
return 'done' for x in fib(5):
print(x) #输出结果如下:
1
1
2
3
5

但是这里有个问题就是用for循环调用generator时就无法获取到生成器中的return返回值了,如果想要获取返回值,还得借助next函数并捕获StopIteration异常,返回值则包含在StopIteration的value中,如下:

g = fib(5)
while True:
try:
x = next(g)
print(x)
except StopIteration as e:
print("生成器的return返回值:%s" % e.value)
break #输出结果:
1
1
2
3
5
生成器的return返回值:done

3. yield关键字

接下来说一下yield关键字,这里暂时可以把yield理解为return,然后我们用一个列子来说明yield与return的区别。

def fun():
print("====start=====")
while True:
res = yield 8
print('res: %s' % res) g = fun()
print(next(g))
print('*'*20)
print(next(g)) #输出结果如下
====start=====
8
********************
None
8

代码解读:首先创建一个生成器。接下来当程序运行到g = fun()时,实际上并不会去执行fun里面的代码,而是相当于创建了一个生成器对象并赋值给g。程序继续往下执行,当运行到第一个print(next(g))时,因为遇到了next函数,fun中的代码开始执行,首先打印输出====start=====,然后进入while循环执行yield 8,前面说过yield有return的功能,这时程序将数字8return出去,然后程序停止,fun中yield后面的代码不会被执行(注意这里执行完yield 8之后,fun就已经停止往后运行,并不会执行赋值操作,也就是说没有将8赋值给res),所以这里我们看到的输出结果是====start=====和数字8

第二步程序继续执行print(‘*’*20)然后输出20个*

第三步当程序遇到第二个next函数时,跟上面那个next差不多,但不同的是,这个时候程序将从上一个next停止的地方开始执行,也就是说程序又跳回到res = yield 8这一行并且开始执行赋值操作,然而在刚刚执行第一个next的时候=右边的值已经被return出去了并没有执行赋值操作,所以这个时候右边并没有值,因此这个时候res被赋值为None,所以我们看到程序输出为None

第四步程序继续执行while循环,又遇到yield关键字然后同样将8return出去程序停止,所以None后面又输出一个8

这就是yield被return的区别,带yield函数就是一个生成器,生成器有个next函数,当第一次调用next函数时,程序将从生成器fun的开始执行,当继续调用next 时,这一次的next 将从上一次的next停止的地方开始执行,也就是从yield关键字的地方开始执行,而并不是每次next都从生成器的开始执行,然后遇到yield后把要生成的值return出去程序停止(这里的程序停止指的是生成器中yield后面的代码不会执行,而不是整个程序停止)。

那么如果我们想要接收res的值应该怎么办呢?接下来介绍生成器的另外一个函数send函数。

4. send函数

我们将上面的代码修改一下

def fun():
print("====start=====")
while True:
res = yield 8
print('res: %s' % res) g = fun()
print(next(g))
print('*'*20)
print(g.send(5)) #输出结果如下
====start=====
8
********************
5
8

看一下这里只是将原来第一个print(next(g))改为print(g.send(5)),那么res输出结果由原来的None变成了5。这是为什么呐,原来在调用send函数时会将所传递的参数发送给生成器并赋值给res,上面说过return的时候并没有把8赋值给res,下次执行的时候由于值已经被return出去所以只好将None赋值给res。而如果用send的话,开始执行的时候与next一样,也是先从上一次停止的地方开始执行,不同的是send会先把发送过去的参数5赋值给res,然后再继续往后执行,遇见下一回的yield,return出结果后结束。

总结

生成器是这样一个函数,它记住上一次返回时在函数体中的位置,对生成器函数的第二次或第n次调用跳转至该函数上次停止的地方,而上次调用的所有局部变量都保持不变。

生成器的特点:

1. 节约内存

2. 迭代到下一次调用时,所使用的参数都是第一次所保留下的,也就是说在整个函数调用的所有的参数都是第一次调用所保留的而不是新创建的。

Python核心编程之生成器的更多相关文章

  1. Python核心编程的四大神兽:迭代器、生成器、闭包以及装饰器

      生成器 生成器是生成一个值的特殊函数,它具有这样的特点:第一次执行该函数时,先从头按顺序执行,在碰到yield关键字时该函数会暂停执行该函数后续的代码,并且返回一个值:在下一次调用该函数执行时,程 ...

  2. python核心编程第二版笔记

    python核心编程第二版笔记由网友提供:open168 python核心编程--笔记(很详细,建议收藏) 解释器options:1.1 –d   提供调试输出1.2 –O   生成优化的字节码(生成 ...

  3. 学习《Python核心编程》做一下知识点提要,方便复习(一)

    学习<Python核心编程>做一下知识点提要,方便复习. 计算机语言的本质是什么? a-z.A-Z.符号.数字等等组合成符合语法的字符串.供编译器.解释器翻译. 字母组合后产生各种变化拿p ...

  4. python核心编程--笔记

    python核心编程--笔记 的解释器options: 1.1 –d   提供调试输出 1.2 –O   生成优化的字节码(生成.pyo文件) 1.3 –S   不导入site模块以在启动时查找pyt ...

  5. Python核心编程第二版(中文).pdf 目录整理

    python核心编程目录 Chapter1:欢迎来到python世界!-页码:7 1.1什么是python 1.2起源  :罗萨姆1989底创建python 1.3特点 1.3.1高级 1.3.2面向 ...

  6. python核心编程--笔记(不定时跟新)(转)

    的解释器options: 1.1 –d   提供调试输出 1.2 –O   生成优化的字节码(生成.pyo文件) 1.3 –S   不导入site模块以在启动时查找python路径 1.4 –v   ...

  7. python核心编程笔记(转)

    解释器options: 1.1 –d   提供调试输出 1.2 –O   生成优化的字节码(生成.pyo文件) 1.3 –S   不导入site模块以在启动时查找python路径 1.4 –v   冗 ...

  8. Python核心编程(第二版)PDF

    Python核心编程(第二版) 目录 第1部分 Python核心第1章 欢迎来到Python世界1.1 什么是Python1.2 起源1.3 特点1.3.1 高级1.3.2 面向对象1.3.3 可升级 ...

  9. 拒绝从入门到放弃_《Python 核心编程 (第二版)》必读目录

    目录 目录 关于这本书 必看知识点 最后 关于这本书 <Python 核心编程 (第二版)>是一本 Python 编程的入门书,分为 Python 核心(其实并不核心,应该叫基础) 和 高 ...

随机推荐

  1. GO练习题

    package main import( "fmt" ) func list(n int) { for i := 0; i <= n; i++ { fmt.Printf(&q ...

  2. Java 内存模型(Java Memory Model,JMM)

    基本概念 JMM 本身是一种抽象的概念并不是真实存在,它描述的是一组规范,通过这组规范定义了程序的访问方式 JMM 同步规定 线程解锁前,必须把共享变量的值刷新回主内存 线程加锁前,必须读取主内存的最 ...

  3. 从SpringBoot源码看资源映射原理

    前言 很多的小伙伴刚刚接触SpringBoot的时候,可能会遇到加载不到静态资源的情况. 比如html没有样式,图片无法加载等等. 今天王子就与大家一起看看SpringBoot中关于资源映射部分的主要 ...

  4. SSRF漏洞(原理、漏洞利用、修复建议)

    介绍SSRF漏洞 SSRF (Server-Side Request Forgery,服务器端请求伪造)是一种由攻击者构造请求,由服务端发起请求的安全漏洞.一般情况下,SSRF攻击的目标是外网无法访问 ...

  5. 我要吹爆这份阿里中间件技术内部的RM笔记,简直佩服到五体投地

    消息队列 RocketMQ 版是阿里云基于 Apache RocketMQ 构建的低延迟.高并发.高可用.高可靠的分布式消息中间件.该产品最初由阿里巴巴自研并捐赠给 Apache 基金会,服务于阿里集 ...

  6. Spark 模型选择和调参

    Spark - ML Tuning 官方文档:https://spark.apache.org/docs/2.2.0/ml-tuning.html 这一章节主要讲述如何通过使用MLlib的工具来调试模 ...

  7. 智慧矿山-选矿工艺数字 3D 可视化

    前言 现代科技和工业的发展对矿物原料的要求越来越高,直接开采的原矿石往往达不到标准,而原矿通过选矿加工后则可以满足要求.选矿技术在冶金.煤炭.化工.建材和环保等部门都得到应用,对国民经济的发展意义重大 ...

  8. SpringBoot-03-JSR303数据校验和多环境切换

    3.3 JSR303数据校验 先看如何使用 ​ Springboot中可以用@Validated来校验数据,如果数据异常则统一抛出异常,方便异常中心统一处理. ​ 这里我们写个注解让name只支持Em ...

  9. Android作业0930

    1.使用ListView和Adapter实现购物商城 Android 布局文件 <?xml version="1.0" encoding="utf-8"? ...

  10. LRU Cache & Bloom Filter

    Cache 缓存 1. 记忆 2. 空间有限 3. 钱包 - 储物柜 4. 类似背代码模板,O(n) 变 O(1)     LRU Cache 缓存替换算法 1. Least Recently Use ...