小白学 Python(21):生成器基础

人生苦短,我选Python
前文传送门
生成器
我们前面聊过了为什么要使用迭代器,各位同学应该还有印象吧(说没有的就太过分了)。

列表太大的话会占用过大的内存,可以使用迭代器,只拿出需要使用的部分。
生成器的设计原则和迭代器是相似的,如果需要一个非常大的集合,不会将元素全部都放在这个集合中,而是将元素保存成生成器的状态,每次迭代的时候返回一个值。
比如我们要生成一个列表,可以采用如下方式:
list1 = [x*x for x in range(10)]
print(list1)
结果如下:
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
如果我们生成的列表非常的巨大,比如:
list2 = [x*x for x in range(1000000000000000000000000)]
结果如下:
Traceback (most recent call last):
File "D:/Development/Projects/python-learning/base-generator/Demo.py", line 3, in <module>
list2 = [x*x for x in range(1000000000000000000000000)]
File "D:/Development/Projects/python-learning/base-generator/Demo.py", line 3, in <listcomp>
list2 = [x*x for x in range(1000000000000000000000000)]
MemoryError
报错了,报错信息提示我们存储异常,并且整个程序运行了相当长一段时间。友情提醒,这么大的列表创建请慎重,如果电脑配置不够很有可能会将电脑卡死。
如果我们使用生成器就会非常方便了,而且执行速度嗖嗖的。

generator1 = (x*x for x in range(1000000000000000000000000))
print(generator1)
print(type(generator1))
结果如下:
<generator object <genexpr> at 0x0000014383E85B48>
<class 'generator'>
那么,我们使用了生成器以后,怎么读取生成器生成的数据呢?
当然是和之前的迭代器一样的拉,使用 next() 函数:
generator2 = (x*x for x in range(3))
print(next(generator2))
print(next(generator2))
print(next(generator2))
print(next(generator2))
结果如下:
Traceback (most recent call last):
File "D:/Development/Projects/python-learning/base-generator/Demo.py", line 14, in <module>
print(next(generator2))
StopIteration
直到最后,抛出 StopIteration 异常。
但是,这种使用方法我们并不知道什么时候会迭代结束,所以我们可以使用 for 循环来获取每生成器生成的具体的元素,并且使用 for 循环同时也无需关心最后的 StopIteration 异常。
generator3 = (x*x for x in range(5))
for index in generator3:
print(index)
结果如下:
0
1
4
9
16
generator 非常的强大,本质上, generator 并不会取存储我们的具体元素,它存储是推算的算法,通过算法来推算出下一个值。
如果推算的算法比较复杂,用类似列表生成式的 for 循环无法实现的时候,还可以用函数来实现。
比如我们定义一个函数,emmmmmm,还是简单点吧,大家领会精神:

def print_a(max):
i = 0
while i < max:
i += 1
yield i
a = print_a(10)
print(a)
print(type(a))
结果如下:
<generator object print_a at 0x00000278C6AA5CC8>
<class 'generator'>
这里使用到了关键字 yield , yield 和 return 非常的相似,都可以返回值,但是不同的是 yield 不会结束函数。
我们调用几次这个用函数创建的生成器:
print(next(a))
print(next(a))
print(next(a))
print(next(a))
结果如下:
1
2
3
4
可以看到,当我们使用 next() 对生成器进行一次操作的时候,会返回一次循环的值,在 yield 这里结束本次的运行。但是在下一次执行 next() 的时候,会接着上次的断点接着运行。直到下一个 yield ,并且不停的循环往复,直到运行至生成器的最后。
还有一种与 next() 等价的方式,直接看示例代码吧:
print(a.__next__())
print(a.__next__())
结果如下:
5
6
接下来要介绍的这个方法就更厉害了,不仅能迭代,还能给函数再传一个值回去:
def print_b(max):
i = 0
while i < max:
i += 1
args = yield i
print('传入参数为:' + args)
b = print_b(20)
print(next(b))
print(b.send('Python'))
结果如下:
1
传入参数为:Python
2
上面讲了这么多,可能各位还没想到生成器能有什么具体的作用吧,这里我来提一个——协程。
在介绍什么是协程之前先介绍下什么是多线程,就是在同一个时间内可以执行多个程序,简单理解就是你平时可能很经常的一边玩手机一边听音乐(毫无违和感)。

协程更贴切的解释是流水线,比如某件事情必须 A 先做一步, B 再做一步,并且这两件事情看起来要是同时进行的。

def print_c():
while True:
print('执行 A ')
yield None
def print_d():
while True:
print('执行 B ')
yield None
c = print_c()
d = print_d()
while True:
c.__next__()
d.__next__()
结果如下:
...
执行 A
执行 B
执行 A
执行 B
执行 A
执行 B
执行 A
执行 B
执行 A
执行 B
...
因为 while 条件设置的是永真,所以这个循环是不会停下来的。
这里我们定义了两个生成器,并且在一个循环中往复的调用这两个生成器,这样看起来就是两个任务在同时执行。
最后的协程可能理解起来稍有难度,有问题可以在公众号后台问我哦~~~
示例代码
本系列的所有代码小编都会放在代码管理仓库 Github 和 Gitee 上,方便大家取用。
小白学 Python(21):生成器基础的更多相关文章
- 小白学 Python(23):Excel 基础操作(上)
人生苦短,我选Python 前文传送门 小白学 Python(1):开篇 小白学 Python(2):基础数据类型(上) 小白学 Python(3):基础数据类型(下) 小白学 Python(4):变 ...
- 小白学 Python(24):Excel 基础操作(下)
人生苦短,我选Python 前文传送门 小白学 Python(1):开篇 小白学 Python(2):基础数据类型(上) 小白学 Python(3):基础数据类型(下) 小白学 Python(4):变 ...
- 小白学 Python 爬虫(21):解析库 Beautiful Soup(上)
小白学 Python 爬虫(21):解析库 Beautiful Soup(上) 人生苦短,我用 Python 前文传送门: 小白学 Python 爬虫(1):开篇 小白学 Python 爬虫(2):前 ...
- 小白学 Python(5):基础运算符(上)
人生苦短,我选Python 前文传送门 小白学 Python(1):开篇 小白学 Python(2):基础数据类型(上) 小白学 Python(3):基础数据类型(下) 小白学 Python(4):变 ...
- 小白学 Python 爬虫(30):代理基础
人生苦短,我用 Python 前文传送门: 小白学 Python 爬虫(1):开篇 小白学 Python 爬虫(2):前置准备(一)基本类库的安装 小白学 Python 爬虫(3):前置准备(二)Li ...
- 小白学 Python 爬虫(32):异步请求库 AIOHTTP 基础入门
人生苦短,我用 Python 前文传送门: 小白学 Python 爬虫(1):开篇 小白学 Python 爬虫(2):前置准备(一)基本类库的安装 小白学 Python 爬虫(3):前置准备(二)Li ...
- 小白学 Python 爬虫(33):爬虫框架 Scrapy 入门基础(一)
人生苦短,我用 Python 前文传送门: 小白学 Python 爬虫(1):开篇 小白学 Python 爬虫(2):前置准备(一)基本类库的安装 小白学 Python 爬虫(3):前置准备(二)Li ...
- 小白学 Python 爬虫(34):爬虫框架 Scrapy 入门基础(二)
人生苦短,我用 Python 前文传送门: 小白学 Python 爬虫(1):开篇 小白学 Python 爬虫(2):前置准备(一)基本类库的安装 小白学 Python 爬虫(3):前置准备(二)Li ...
- 小白学 Python 爬虫(35):爬虫框架 Scrapy 入门基础(三) Selector 选择器
人生苦短,我用 Python 前文传送门: 小白学 Python 爬虫(1):开篇 小白学 Python 爬虫(2):前置准备(一)基本类库的安装 小白学 Python 爬虫(3):前置准备(二)Li ...
随机推荐
- c++ exercises
1.定义个数组并初始化,然后将数组倒置,并显示倒置前和倒置后的结果. #include <iostream> #include <stdio.h> using namespac ...
- File类&递归
File类1.什么是file类Java中处理操作系统文件的类.2.file思想创建一个File对象,代表了操作系统的具体的一个文件(文件,文件夹)然后通过这个File对象就可以操作该文件:删除该文件, ...
- http服务端架构演进
摘要 在详解http报文相关文章中我们介绍了http协议是如何工作的,那么构建一个真实的网站还需要引入组件呢?一些常见的名词到底是什么含义呢? 什么叫正向代理,什么叫反向代理 服务代理与负载均衡的差别 ...
- 关于thinkphp框架中模型笔记
模型这一块,感觉学习的不是很清楚,单独水一贴thinkphp中模型的学习笔记. 0x01 模型类简介 数据库中每一张表对应一个模型,类名就是表名,类里面的成员变量就是列名, 把一张表对应为一个类,其中 ...
- shark恒破解笔记6-摆脱NAG
1.打开软件后,发现是未注册,然后点击关闭按钮,会弹出窗口 我们的目的就是为了能够去掉这个弹窗. 2.对这个程序进行查壳,没有什么发现 3.载入OD里面,F9运行起来,随后切换到程序主界面点击关闭按钮 ...
- Fibonacci 数列和 Lucas 数列的性质、推论及其证明
Fibonacci 数列 设f(x)=1,x∈{1,2}=f(x−1)+f(x−2),x∈[3,∞)\begin{aligned}f(x)&=1,\quad\quad\quad\quad\qu ...
- Cocos2d-x 学习笔记(11.2) RotateBy RotateTo
1. RotateBy RotateTo 两个旋转方法.RotateBy是在当前角度上旋转设置的角度.RotateTo是直接旋转到设置的角度,方向遵循“就近原则”.两者没有相互继承关系. 1.1 成员 ...
- 一篇文章彻底搞懂snowflake算法及百度美团的最佳实践
写在前面的话 一提到分布式ID自动生成方案,大家肯定都非常熟悉,并且立即能说出自家拿手的几种方案,确实,ID作为系统数据的重要标识,重要性不言而喻,而各种方案也是历经多代优化,请允许我用这个视角对分布 ...
- 【RabbitMQ 实战指南】一 RabbitMQ 开发
1.RabbitMQ 安装 RabbitMQ 的安装可以参考官方文档:https://www.rabbitmq.com/download.html 2.管理页面 rabbitmq-management ...
- 利用SpringBoot+Logback手写一个简单的链路追踪
目录 一.实现原理 二.代码实战 三.测试 最近线上排查问题时候,发现请求太多导致日志错综复杂,没办法把用户在一次或多次请求的日志关联在一起,所以就利用SpringBoot+Logback手写了一个简 ...