如有问题欢迎指出!
Python中生成器是迭代器(Iterator) 的一种,每次遇到 yield
时函数会暂停并保存当前所有的运行信息,返回 yield
后的值, 并在下一次执行 next()
方法时从当前位置继续运行。
列表一次生成一组值,占用内存空间;生成器一次只产生一个值,用多少,取多少。
如何创建生成器?
yield
关键字的函数——生成器函数,调用函数就是创建了一个生成器(generator)对象。yield
相当于return
返回一个值,并且记住这个返回的位置保留当前信息,下次迭代时,代码从yield
的下一条语句继续执行。
这里大家可以思考下将generator_2(n)
中的yield
改为return
,每次调用函数的结果是什么。
# 列表生成式
list1 = [x*x for x in range(3)]
print(list1)
# 创建生成器
# 1.生成器表达式:列表生成式的中括号[]改为小括号()
generator1 = (x*x for x in range(3))
print(generator1)
# 2.函数中包含yield关键字,调用函数就是创建了一个生成器(generator)对象。
def generator_2(n):
for x in range(n):
yield x*x
generator2 =generator_2(3)
print(generator2)
Output:
[0, 1, 4]
at 0x000001F4C81E1AC0>
注意:调用生成器函数时generator2 =generator_2(3)
只是创建了一个生成器,并不会执行函数里面的内容。
next()
方法 ,直到捕获一个异常;next()
方法:next(generator)
或generator.__next__()
def generator_2(n):
for x in range(n):
yield x*x
generator2 =generator_2(3)
print(generator2)
print(next(generator2))
print(generator2.__next__())
Output:
0
1
print(next(generator1))
print(next(generator1))
print(next(generator1))
print(next(generator1))
Output:
通过重复调用next(generaotr_1)
对生成器进行迭代得到相应值,直到得到最后一个元素。
当没有更多的元素时,继续使用next()
会出现StopIteration的错误。
推荐使用for循环对生成器进行迭代。
for i in generator1:
print(i)
(根据《算法精粹》改编)
斐波那契数列指的是这样一个数列:0,1、1、2、3、5、8、13、21、34、55……前两项为 f i b ( 0 ) = 0 fib(0)=0 fib(0)=0、 f i b ( 1 ) = 1 fib(1)=1 fib(1)=1,后面的每一项都等于它前两项之和,即 f i b ( n ) = f i b ( n − 1 ) + f i b ( n − 2 ) fib(n)=fib(n-1)+fib(n-2) fib(n)=fib(n−1)+fib(n−2)。
def fib(n:int):
yield 0
if n>0:
yield 1
if n>1:
n_2,n_1 = 0,1
for _ in range(2,n+1):
n_2,n_1 = n_1,n_2+n_1
yield n_1
return 'done'
for i in fib(5):
print(i)
Ouput:
0
1
1
2
3
5
根据上面的输出结果,发现通过for循环对生成器进行迭代,得不到return语句的返回值。
如果想得到returnd的返回值,需捕获StopIteration错误,返回值包含在StopIteration的value中。
def fib(n:int):
yield 0
if n>0:
yield 1
if n>1:
n_2,n_1 = 0,1
for _ in range(2,n+1):
n_2,n_1 = n_1,n_2+n_1
yield n_1
return 'done'
g = fib(5)
while True:
try:
x = next(g)
print(x)
except StopIteration as e:
print("return返回值:",e.value)
break
Output:
0
1
1
2
3
5
return返回值: done
next()
与.send()
的区别参考:
python中yield的用法详解–最简单,最清晰的解释_mieleizhi0522的博客-CSDN博客_yield
两句话总结: 区别:send
可传递参数给yield
表达式,传递的参数就会作为yield
表达式的值,也就是强行修改上一个yield
表达式值。二者返回值:当前迭代遇到的yield
时,yield
后面表达式的值。
next(generator)
或generator.__next__()
def foo():
print("starting...")
while True:
res = yield 4
print("res:",res)
g = foo() # 创建一个生成器并不执行函数内的内容
print(g)
print(next(g)) # = g.__next__()
print("*"*20)
print(next(g))
Output:
starting...
4
********************
res: None
4
程序运行步骤:
g = foo()
因foo函数中有yield
关键字,调用该函数会创建一个生成器对象,不会真的执行该函数,因此print(g)
的结果是:next()
方法,foo函数正式开始执行,先执行foo函数中的 print("starting...")
得到输出:starting…;while
循环,遇到yield 4
,在此暂停,返回值4,next(g)
语句执行完成,得到输出:4,注意这时候没有赋值给res
;print("*"*20)
;print(next(g))
,进入foo函数,从上回暂停的位置继续执行,即执行res的赋值操作,注意这时赋值操作的右边是没有值的(因为刚才的4直接返回出去了,并没有给赋值操作的左边传参数),所以这个时候res
赋值是None
,print("res:",res)
结果为res: None;while
循环中执行,直到再一次遇到yield
,返回4,暂停,print(next(g))
的输出就是函数的返回值4。.send()
.send()
会传入一个值作为yield
表达式整体的结果,即覆盖掉上一个yield
表达式值。
def foo():
print("starting...")
while True:
res = yield 4
print("res:",res)
g = foo() # 创建一个函数并不执行
print(g)
print(next(g)) # = g.__next__()
print("*"*20)
print(g.send(7))
Output:
starting...
4
********************
res: 7
4
程序运行步骤:(前4步与next()
相同)
g = foo()
因foo函数中有yield
关键字,调用该函数会创建一个生成器对象,不会真的执行该函数,因此print(g)
的结果是:next()
方法,foo函数正式开始执行,先执行foo函数中的 print("starting...")
得到输出:starting…;while
循环,遇到yield 4
,在此暂停,返回值4,next(g)
语句执行完成,得到输出:4,注意这时候没有赋值给res
;print("*"*20)
;g.send(7)
,进入foo函数,从上回暂停的位置继续执行,.send(7)
传入7覆盖掉上一次的yield
表达式,这时相当于res = 7
,print("res:",res)
结果为res: 7;while
循环中执行,直到再一次遇到yield
,返回4,暂停,print(next(g))
的输出就是函数的返回值4。补充一点迭代器和可迭代对象,后续写一节完整的。
for
循环的对象都是Iterable
类型,用isinstance()
判断一个对象是否为可Iterable;next()
函数的对象都是Iterator
类型,它们表示一个惰性计算的序列,可用isinstance()
判断一个对象是否是Iterator对象;list
、dict
、str
等是Iterable
但不是Iterator
,不过可以通过iter()
函数获得一个Iterator
对象。