生成器
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。
而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,
那后面绝大多数元素占用的空间都白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?
这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]
改成()
,就创建了一个generator:
def fib(max): n, a, b = 0, 0, 1 while n < max: #print(b) yield b #只要有它在,这里就不是函数了,是一个生成器了。 把fib函数变成generator了。 a, b = b, a + b # 相当于 t = (b, a + b) t是一个tuple a = t[0] b = t[1] n = n + 1 return '---yyyy----' f=fib(10) print(f.__next__()) print(f.__next__()) print(f.__next__()) print(f.__next__()) print("------") for i in f: print(i)
我们在循环过程中不断调用yield
,就会不断中断。当然要给循环设置一个条件来退出循环,不然就会产生一个无限数列出来。
同样的,把函数改成generator后,我们基本上从来不会用next()
来获取下一个返回值,而是直接使用for
循环来迭代:
for n in fib(6): print(n)
但是用for
循环调用generator时,发现拿不到generator的return
语句的返回值。
如果想要拿到返回值,必须捕获StopIteration
错误,返回值包含在StopIteration
的value
中:
def fib(max):
n, a, b = 0, 0, 1
while n < max:
#print(b)
yield b #只要有它在,这里就不是函数了,是一个生成器了。 把fib函数变成generator了。
a, b = b, a + b # 相当于 t = (b, a + b) t是一个tuple a = t[0] b = t[1]
n = n + 1
return '---yyyy----'
g= fib(6)
while True:
try:
x = next(g)
print("g:",x)
except StopIteration as e:
print("Generator return value:",e.value)
break
最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return
语句或者最后一行函数语句就返回。
而变成generator的函数,在每次调用next()
的时候执行,遇到yield
语句返回,再次执行时从上次返回的yield
语句处继续执行data = fib(print(data)
print(data.__next__()) print(data.__next__()) print("干点别的事") print(data.__next__()) print(data.__next__()) print(data.__next__()) print(data.__next__()) print(data.__next__()) #输出 <generator object fib at 0x101be02b0> 1
1
干点别的事
2 3 5 8 13