Python生成器
生成器详解
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被调用----
...