人生苦短, 我选 Python
前文传送门
小白学 Python(1): 开篇 https://www.geekdigging.com/2019/10/12/2870915863/
小白学 Python(2): 基础数据类型 (上) https://www.geekdigging.com/2019/10/13/2870915864/
小白学 Python(3): 基础数据类型 (下) https://www.geekdigging.com/2019/10/15/25090937/
小白学 Python(4): 变量基础操作 https://www.geekdigging.com/2019/10/17/2392286754/
小白学 Python(5): 基础运算符 (上) https://www.geekdigging.com/2019/10/18/999362065/
小白学 Python(6): 基础运算符 (下) https://www.geekdigging.com/2019/10/20/2416267271/
小白学 Python(7): 基础流程控制 (上) https://www.geekdigging.com/2019/10/22/979409150/
小白学 Python(8): 基础流程控制 (下) https://www.geekdigging.com/2019/10/23/2184234984/
小白学 Python(9): 基础数据结构 (列表)(上) https://www.geekdigging.com/2019/10/23/3066758654/
小白学 Python(10): 基础数据结构 (列表)(下) https://www.geekdigging.com/2019/10/25/1277177083/
小白学 Python(11): 基础数据结构 (元组) https://www.geekdigging.com/2019/10/26/1783681326/
小白学 Python(12): 基础数据结构 (字典)(上) https://www.geekdigging.com/2019/10/27/1304658395/
小白学 Python(13): 基础数据结构 (字典)(下) https://www.geekdigging.com/2019/10/27/2327215405/
小白学 Python(14): 基础数据结构 (集合)(上) https://www.geekdigging.com/2019/10/29/2327215406/
小白学 Python(15): 基础数据结构 (集合)(下) https://www.geekdigging.com/2019/10/30/3105860436/
小白学 Python(16): 基础数据类型 (函数)(上) https://www.geekdigging.com/2019/10/31/3818474124/
小白学 Python(17): 基础数据类型 (函数)(下) https://www.geekdigging.com/2019/11/01/619492986/
小白学 Python(18): 基础文件操作 https://www.geekdigging.com/2019/11/01/3195979051/
小白学 Python(18): 基础文件操作 https://www.geekdigging.com/2019/11/01/3195979051/
小白学 Python(19): 基础异常处理 https://www.geekdigging.com/2019/11/03/271169345/
小白学 Python(20): 迭代器基础 https://www.geekdigging.com/2019/11/05/1111962512/
生成器
我们前面聊过了为什么要使用迭代器, 各位同学应该还有印象吧 (说没有的就太过分了).
列表太大的话会占用过大的内存, 可以使用迭代器, 只拿出需要使用的部分.
生成器的设计原则和迭代器是相似的, 如果需要一个非常大的集合, 不会将元素全部都放在这个集合中, 而是将元素保存成生成器的状态, 每次迭代的时候返回一个值.
比如我们要生成一个列表, 可以采用如下方式:
- 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 上, 方便大家取用.
示例代码 - GitHub
示例代码 - Gitee
来源: https://www.cnblogs.com/babycomeon/p/11854666.html