생성기는 내부적으로 __iter__
및 __next__
메서드를 갖는 특수 반복자입니다. >StopIteration 예외는 루프를 종료하지만 반복자와 비교하여 생성기는 "중간 값"을 저장하는 기능도 있으며 다음에 실행될 때 이 "중간 값"을 사용합니다. 생성기의 키워드는 yield
입니다. 아래에서 가장 간단한 생성기를 작성해 보겠습니다. __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
,哎,不急,我们接着往下看为什么需要生成器,或者说,生成器解决了什么问题。
在说明这个问题之前,我们先来写一个需求,输出 0——10000000 以内的数据,而后运行查看导出内存运行截图。
这里可以借助python
的memory_profiler
模块来检测程序内存的占用情况。
安装memory_profiler
库:
pip3 install memory_profiler
使用方法很简单,在需要检测的函数或者是代码前添加@profile
装饰器即可,例如:
@profile def main(): pass
生成.dat
文件
mprof run
导出图示,可以使用
mprof plot --output=filename
以下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。
通过上述案例,我们应该知道为什么要使用生成器了吧。
由于生成器表达式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
rrreee
range
를 사용하여 생성하지 않고 yield
를 사용하면 될까요? 걱정하지 마세요. 그러면 발전기가 왜 필요한지, 즉 발전기가 어떤 문제를 해결하는지 살펴보겠습니다. 파이썬 생성기가 필요한 이유이 문제를 설명하기 전에 먼저 0-10000000 범위의 데이터를 출력하라는 요구 사항을 작성한 다음 실행하여 내보낸 메모리 작업의 스크린샷을 살펴보겠습니다.
python
의 memory_profiler
모듈을 사용하여 프로그램의 메모리 사용량을 감지할 수 있습니다. 🎜🎜memory_profiler
라이브러리 설치: 🎜rrreee🎜 사용법은 매우 간단합니다. 감지해야 하는 함수나 코드 앞에 @profile
데코레이터를 추가하면 됩니다. 예를 들면 다음과 같습니다. 🎜rrreee🎜 .dat
파일 생성 🎜🎜mprof run🎜다이어그램을 내보내려면 🎜🎜
🎜mprof 플롯 --output=을 사용할 수 있습니다. filename🎜
range
를 사용한 다음 list
에 >append를 추가하고 두 번째는 반복자를 사용하여 데이터를 생성합니다. 🎜🎜main.py
Program🎜rrreee🎜main_2.py
Program🎜rrreeemain.py
메모리 그래프 실행🎜🎜🎜🎜main_2.py
메모리 그래프 실행🎜🎜🎜🎜위의 두 비교사진처럼 겹쳐보면 데이터를 목록에 추가하고 출력할 때 거의 400M의 메모리를 차지하지만 반복자를 사용하여 다음 값을 계산하는 데는 16M의 메모리만 사용됩니다. 🎜🎜위의 사례를 통해 우리가 발전기를 사용하는 이유를 알아야 합니다. 🎜🎜Python 생성기 원리🎜🎜생성기 표현식 yield
문은 python
해석 능력의 내부 메커니즘을 포함하기 때문에 소스 코드를 보고 원리를 파악하기가 어렵습니다. , yield
의 일시 중지 메커니즘을 사용하여 생성기를 탐색할 수 있습니다. 🎜🎜다음 코드를 작성할 수 있습니다. 🎜rrreee🎜실행 후 효과는 다음과 같습니다🎜🎜🎜🎜위의 예를 통해 다음 생성기 작동 과정과 결합하여 생성기에 대한 이해가 깊어질 것입니다. 🎜🎜python
이 yield
문을 발견하면 현재 함수의 실행 상태를 기록하고 실행을 일시 중지한 다음 결과를 발생시킵니다. __next__
메서드에 대한 다음 호출을 계속 기다립니다. 이 메서드가 호출된 후 함수는 다음 yield
문이나 함수가 끝날 때까지 실행을 재개합니다. . 실행이 끝나면 가 없습니다. 항복
함수가 실행 가능하면 생성기의 끝을 표시하기 위해 StopIteration
이 발생합니다. 🎜🎜생성기 표현식🎜🎜python
에서는 함수에 생성기를 작성하고 yield
를 사용하여 반환하는 것 외에도 생성기 표현식을 직접 사용할 수도 있습니다. . . 추상적일 수도 있지만, 아래 코드를 보시면 이해가 되실 겁니다. 🎜rrreee🎜그 중 (i for i in [1,2,3,4,5])
코드는 printNums
함수와 동일하며 해당 유형은 다음과 같습니다. type
을 사용하여 출력하여 살펴볼 수 있습니다. 🎜🎜코드를 변경하면 출력 결과는 다음과 같습니다.🎜🎜🎜🎜위 내용은 Python의 생성기는 어떻게 작동하나요?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!