生成器

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. Python反转链表

    # -*- coding:utf-8 -*- # class ListNode: # def __init__(self, x): # self.val = x # self.next = None ...

  2. Spring学习(三)Spring AOP 简介

    一.简介 定义 aop就是面向切面编程,在数据库事务中切面编程被广泛使用. 在面向切面编程的思想里面,把功能分为核心业务功能,和周边功能. 核心业务:比如登陆,增加数据,删除数据都叫核心业务 周边功能 ...

  3. Java8——Stream流式操作的一点小总结

    我发现,自从我学了Stream流式操作之后,工作中使用到的频率还是挺高的,因为stream配合着lambda表达式或者双冒号(::)使用真的是优雅到了极致!今天就简单分(搬)享(运)一下我对strea ...

  4. C++实现职工管理系统(中)

    C++实现职工管理系统(中) 大家好,今天是在博客园的第九天,博主今天给大家带来的是职工管理系统(C++)(中) 这次的随笔记录是实现(上)结语处说的几个功能 目录 C++实现职工管理系统(中) 1. ...

  5. 日志分析平台ELK之搜索引擎Elasticsearch集群

    一.简介 什么是ELK?ELK是Elasticsearch.Logstash.Kibana这三个软件的首字母缩写:其中elasticsearch是用来做数据的存储和搜索的搜索引擎:logstash是数 ...

  6. Python练习题 009:水仙花数

    [Python练习题 009] 打印出所有的"水仙花数",所谓"水仙花数"是指一个三位数,其各位数字立方和等于该数本身.例如:153是一个"水仙花数& ...

  7. Spring循环依赖的问题

      什么是循环依赖?就是两个Bean相互引用,比如用@Autowire 相互注入.   那么Spring是如何解决这个问题的呢?在Bean还未完全实例化前(类只实例化了一部分),将bean提前暴露出来 ...

  8. Spring中用@DependsOn注解控制Bean的创建顺序

    1. 概述 Spirng容器自己会管理bean的生命周期和bean实例化的顺序,但是我们仍然可以根据我们自己的需求进行定制.我可以可以选择使用SmartLifeCycle接口,也可以用@Depends ...

  9. 007 01 Android 零基础入门 01 Java基础语法 02 Java常量与变量 01 Java标识符

    007 01 Android 零基础入门 01 Java基础语法 02 Java常量与变量 01 Java标识符 Java变量与常量主要内容 Java变量与常量主要内容如下,主要是对以下内容的学习,没 ...

  10. Arduino 多线程简单代码

    转载: 1.   https://www.csdn.net/gather_27/MtTaggzsMDExMS1ibG9n.html 2.   https://v.youku.com/v_show/id ...