生成器详解
1.什么是迭代器
只要定义了__iter__
的类那么他就是一个可迭代对象
,如果定义了__next__
方法那么他就是一个迭代器.
2.迭代器与生成器的区别
生成器
是一种迭代器
,但是迭代器
不一定是生成器
3.什么是生成器
简单理解为每次生出(yield)一个值的东西
例子:
def generator(): for i in range(3): yield i
这个函数就是一个生成器,与普通函数不同的是,当函数执行到yield i
语句时,函数会被挂起并且把yield
后面的i
给返回,当你再次执行该函数时会从yield
语句后面接着执行
不同于return
语句的是,return
语句不会挂起
4.生成器的优点
由于生成器每调用一次就只执行一次,所以生成器占用得到内存就会非常少,看个循环100000000次的例子
In [185]: def func(): ...: for i in range(100000000): ...: pass ...: In [186]: def gene(): ...: for i in range(100000000): ...: yield ...: In [187]: def getTime(f): ...: start = time.time() ...: f() ...: end = time.time() ...: print(end-start) ...: In [188]: getTime(func) 2.0609002113342285 In [189]: getTime(gene) 2.6226043701171875e-06
5.生成器的使用
可以使用next()
函数或者__next__()
方法来取值,当到最后一个值时会产生异常.
也可使用for
语句取值,并且for
语句会自动处理这个异常.
next()
例子:
In [146]: def generator(): ...: for i in range(3): ...: yield i ...: In [147]: g = generator() In [148]: next(g) Out[148]: 0 In [149]: next(g) Out[149]: 1 In [150]: next(g) Out[150]: 2 In [151]: next(g) --------------------------------------------------------------------------- StopIteration Traceback (most recent call last) <ipython-input-151-e734f8aca5ac> in <module> ----> 1 next(g) StopIteration:
__next__()
例子:
In [146]: def generator(): ...: for i in range(3): ...: yield i ...: In [147]: g = generator() In [148]: next(g) Out[148]: 0 In [149]: next(g) Out[149]: 1 In [150]: next(g) Out[150]: 2 In [151]: next(g) --------------------------------------------------------------------------- StopIteration Traceback (most recent call last) <ipython-input-151-e734f8aca5ac> in <module> ----> 1 next(g) StopIteration:
for
例子:
In [146]: def generator(): ...: for i in range(3): ...: yield i ...: In [147]: g = generator() In [148]: next(g) Out[148]: 0 In [149]: next(g) Out[149]: 1 In [150]: next(g) Out[150]: 2 In [151]: next(g) --------------------------------------------------------------------------- StopIteration Traceback (most recent call last) <ipython-input-151-e734f8aca5ac> in <module> ----> 1 next(g) StopIteration:
注意:不要这样写
In [162]: def generator(): ...: for i in range(3): ...: yield i ...: In [163]: next(generator()) Out[163]: 0 In [164]: next(generator()) Out[164]: 0 In [165]: next(generator()) Out[165]: 0
直接调用生成器,每次会从头开始,这样就永远只能取到第一个值.(但是for
循环可以使用)
In [162]: def generator(): ...: for i in range(3): ...: yield i ...: In [166]: for i in generator(): ...: print(i) ...: 0 1 2
6.可迭代对象与迭代器
可迭代对象不一定是迭代器
str
是可迭代对象,但是他不是迭代器:
In [191]: string = "abcdefg" In [192]: for i in string: ...: print(i) ...: a b c d e f g In [193]: next(string) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-193-50005732e4cf> in <module> ----> 1 next(string) TypeError: 'str' object is not an iterator
将可迭代对象转换成迭代器:
In [194]: string = iter(string) In [195]: next(string) Out[195]: 'a' In [196]: next(string) Out[196]: 'b' In [197]: next(string) Out[197]: 'c'
7.send
函数
使用send
函数可以向生成器发送数据,yield
左边的变量会接收:
In [205]: def generator(): ...: for i in range(10): ...: recv = yield i ...: print(recv) ...: In [206]: g = generator() In [207]: next(g) Out[207]: 0 In [208]: next(g) None Out[208]: 1 In [209]: g.send("hahaha") hahaha Out[209]: 2 In [210]: g.__next__() None Out[210]: 3 In [211]: g.__next__() None Out[211]: 4
对输出结果的解释:
为什么第一次调用
next
没有打印东西?因为代码在
yield
语句处挂起,没有执行到print
语句为什么有些地方得到输出是
None
None
是print
语句的输出结果,输出为None
是因为recv
变量没有接收到数据
注意:
第一次执行就使用send
会报错:
In [213]: def generator(): ...: for i in range(10): ...: recv = yield i ...: print(recv) ...: In [214]: g = generator() In [215]: g.send("hahahaha") --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-215-046ffd60433c> in <module> ----> 1 g.send("hahahaha") TypeError: can't send non-None value to a just-started generator
错误提示为:不能将一个非None
的数据传给刚开始的生成器.
那么就可以这么写
In [216]: g.send(None) Out[216]: 0
注意:什么都不写不代表None
8.协程
协程是一种多任务(让人感觉同一时间发生了多种事件)的实现方式,生成器可以实现协程
例:
#!/usr/bin/python3 # -*- coding: utf-8 -*- def func1(): while True: yield print("----func1被调用----") def func2(): while True: yield print("----func2被调用----") def main(): f1 = func1() f2 = func2() while True: next(f1) next(f2) if __name__ == '__main__': main()
output:
----func1被调用---- ----func2被调用---- ----func1被调用---- ----func2被调用---- ----func1被调用---- ----func2被调用---- ----func1被调用---- ----func2被调用---- ----func1被调用---- ----func2被调用---- ----func1被调用---- ...
来源:https://www.cnblogs.com/kainhuck/p/11113804.html