分析一个『简单』的Python程序
著名的斐波拉契数列,构造一个生成器类(简单吧。----------------------------------------------------------------
class Fib(object):
def __init__(self, limit):
self.a = 0
self.b = 1
self.limit = limit
def __iter__(self):
return self
def __next__(self):
self.a, self.b = self.b, self.a+self.b
if self.a > self.limit:
raise StopIteration
return self.a #!
def __repr__(self):
return str(self.a)
m = Fib(60)
for i in m:
print(m, end=" ")
==================================================
请看代码中我用#!标出的部分。
试分析如果把这里return换成yield会发生什么。 出现了一连串的000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000……
请解释这一现象。 个人理解,重点在于yield的性质:函数中存在yield时,(例如 def f(x): x+=1 ;yield x; )赋值操作a=f(1)只会把a变成生成器,而不执行函数内的任何语句。直到出现类似next(a)的语句时,才会第一次执行函数,并返回yield后面的值。
运用在这里,由于yield的存在,__next__只给i赋值为生成器,需要等待next(i)或send(i)这样的命令,才会执行生成器i内部的迭代命令,进而执行内部的语句。但是实际上没有此命令,所以每次对m的迭代,都执行不了__next__内的语句,只把重复地把 i 变成生成器。无法执行判断,所以停不下来,m的打印也没变。
而用return则可以正常执行语句,也能做判断结束m的迭代,并把赋值给i。
你可以试试在for语句中把i打印出来。看看两种情形下的i有何不同。
不过其实i被赋予了数值还是生成器,其实本身不影响m的迭代。关键在yield只赋值,不迭代,从而无法执行计算和判断语句,使得m无法被赋新的值,也使得迭代无法停止。
我的话语可能表达不够清楚,但重点有两个:1、yield的作用方式比较特殊,第一次赋值时啥也不执行。 2、在用了yield时,m和i都是迭代器(生成器也是特殊的迭代器),只不过i永远得不到迭代命令,而m一直在迭代。
一个不太好看的解决办法是,把
self.a, self.b = self.b, self.a+self.b
if self.a > self.limit:
raise StopIteration
这一段移到__repr__内。
这样i仍然是一个不被迭代执行的生成器,但m的迭代可以顺利的进行和结束。
同为初学者,我在你这个问题卡了好几个小时,才勉强猜出一个自认为合理的结果,如果能帮到你就再好不过了。如果有问题,也还请指正。 想到一个更好的办法,i不是没有执行迭代吗?那就给他迭代。在for里面加一个 next(i),就行了。
for i in m:
try:
next(i)
print(m, end=" ")
except:
break
页:
[1]