小白学 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开发基础 day14 三元表达式 生成器 匿名函数
- python基础——生成器
- 零基础如何开始学习 Python?看完这篇小白变小牛!
- GitChat·Python | 零基础小白如何入门 Python 编程
- python基础学习13----生成器&迭代器
- python基础-----迭代器、生成器、装饰器
- Python开发基础-Day9-生成器、三元表达式、列表生成式、生成器表达式
- Python基础-生成器和迭代器
- Python 基础 —— 生成器 generator
- python基础:列表生成式和生成器
- python 小白基础篇(三)
- Python基础(9)_生成器(yield表达式形式)、面向过程编程
- python基础课程_学习笔记21:文件和材料
- python基础之协程函数、列表表达式、生成器表达式
- python小白-day4递归和算法基础
- Python小白的发展之路之Python基础(三)【函数简介】
- 零基础如何开始学习 Python?看完这篇小白变小牛!
- 零基础如何开始学习 Python?看完这篇小白变小牛!
- Python小白学习之路——基础语法
- 2015/9/19 Python基础(15):变量作用域及生成器