本文小编为大家详细介绍“Python生成器是怎么工作的”,内容详细,步骤清晰,细节处理妥当,希望这篇“Python生成器是怎么工作的”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。
什么是python生成器
生成器是一种特殊的迭代器,它内部也有
__iter__方法和
__next__方法,在终止生成器的时候,还是会抛
StopIteration异常以此来退出循环,只不过相比于迭代器,生成器还有特性会保存“中间值”,下次运行的时候,还会借助这个“中间值”来操作。生成器的关键字是
yield,我们下面来写一个最简单的生成器。
#!/usr/bin/env python def printNums(): i = 0 while i<10: yield i i = i + 1 def main(): for i in printNums(): print(i) if __name__ == '__main__': main()
粗看代码,可能会觉着这个是个啥啊,为啥不直接用
range来生成,偏偏要用
yield,哎,不急,我们接着往下看为什么需要生成器,或者说,生成器解决了什么问题。
为什么需要python生成器
在说明这个问题之前,我们先来写一个需求,输出 0——10000000 以内的数据,而后运行查看导出内存运行截图。
调用python程序内存信息辅助说明
这里可以借助
python的
memory_profiler模块来检测程序内存的占用情况。
安装
memory_profiler库:
pip3 install memory_profiler
使用方法很简单,在需要检测的函数或者是代码前添加
@profile装饰器即可,例如:
@profile def main(): pass
生成
.dat文件
mprof run <executable>
导出图示,可以使用
mprof plot --output=filename
python案例代码
以下2个程序,都是输出0—9999999之间的数据,不同的是,第一个程序是使用
range而后给
append进
list中,第二个则是使用迭代器来生成该数据。
main.py程序
@profile def main(): data = list(range(10000000)) for i in data: pass if __name__ == '__main__': main()
main_2.py程序
def printNum(): i = 0 while i < 10000000: yield i i = i + 1 @profile def main(): for i in printNum(): pass if __name__ == '__main__': main()
运行程序
代码也有了,就可以按照上述来运行一下程序,并且导出内存信息
运行后内存信息查看
main.py运行内存图
main_2.py运行内存图
如上2张对比图,当我们将数据叠加进列表,再输出的时候,占用内存接近400M,而使用迭代器来计算下一个值内存仅使用16M。
通过上述案例,我们应该知道为什么要使用生成器了吧。
python生成器原理
由于生成器表达式
yield语句涉及到了
python解释权内部机制,所以很难查看其源码,很难获取其原理,不过我们可以利用
yield的暂停机制,来探寻一下生成器。
可以编写如下代码:
def testGenerator(): print("进入生成器") yield "pdudo" print("第一次输出") yield "juejin" print("第二次输出") def main(): xx = testGenerator() print(next(xx)) print(next(xx)) if __name__ == '__main__': main()
运行后效果如下
通过上述实例,再结合下面这段生成器的运行过程,会加深对生成器的感触。
当
python遇到
yield语句时,会记录当前函数的运行状态,并且暂停执行,将结果抛出。会持续等待下一次调用
__next__方法,该方法调用后,会恢复函数的运行,直至下一个
yield语句或者函数结束,执行到最后没有
yield函数可执行的时候,会抛
StopIteration来标志生成器的结束。
生成器表达式
在
python中,生成器除了写在函数中,使用
yield返回之外,还可以直接使用生成器表达式,额。。。可能很抽象,但是你看下面这段代码,你就明白了。
def printNums(): for i in [1,2,3,4,5]: yield i def main(): for i in printNums(): print(i) gener = (i for i in [1,2,3,4,5]) for i in gener: print(i) if __name__ == '__main__': main()
其中,代码
(i for i in [1,2,3,4,5])就等同于
printNums函数,其类型都是生成器,我们可以使用
type打印出来看下。
改下代码,输出结果如下: