一个包含yield关键字的函数就是一个生成器函数。yield可以为我们从函数中返回值,但是yield又不同于return,return的执行意味着程序的结束,调用生成器函数不会得到返回的具体的值,而是得到一个可迭代的对象。每一次获取这个可迭代对象的值,就能推动函数的执行,获取新的返回值。直到函数执行结束。
def generator():print(1)return 'a'ret = generator()
print(ret)
只要含有yield关键字的函数都是生成器函数。
1)yield只能写在函数里;
2)yield不能跟return共用;
def generator():print(1)yield 'a'ret = generator()
print(ret)
结果:
生成器函数:执行之后会得到一个生成器对象作为返回值。
def generator():print(1)yield 'a'ret = generator()
print(ret)print(ret.__next__())
结果:
再看:
def generator():print(1)yield 'a'print(2)yield 'b'g = generator()
print(g)ret = g.__next__()
print(ret)
ret = g.__next__()
print(ret)
结果:
如果再加ret = g.__next__() print(ret)代码,就会发生溢出。会出现迭代器取不到的时候,有stopIteration的错误提示。
我们可以看到函数内部的执行,是受外部控制的。通过yield和__next__两者来对函数进行控制。
def generator():print(1)yield 'a'print(2)yield 'b'g = generator()for i in g:print(i)
既然有__iter__和__next__,那么我们可以使用for循环来管理。
那我们现在看例子:
def hello():for i in range(2000000):yield 'hello %s' % ig = hello()for i in g:print(i)
背后的原理是:这边生产一个,然后就给你一个打印,边生产边打印,这样就不会一下子在内存中产生大量的数据。不是一次性将所有的数据都生成出来,然后再提供给你。这样,就需要大量的内存空间。数据越存越多,越到后面,程序需要去找内存,非常麻烦。
如果只想打印50个:
def hello():for i in range(2000000):yield 'hello %s' % ig = hello()count = 0
for i in g:count += 1print(i)if count > 50:break# print(g.__next__())for i in g:count += 1print(i)if count > 100:break
这个应该有一个位置记录,迭代的过程中记录下一个位置。下一个for循环就从下一个位置开始获取值。
l = [1, 2, 3, 4, 5]for i in l:print(i)if i == 2:breakfor i in l:print(i)
这个例子里说for循环,是生成了一个迭代器。然后for循环结束之后,第二个for循环,迭代器又从头开始执行。
看一个工厂生产衣服的例子:
def produce():"""生产衣服"""for i in range(2000000):yield "生产了第%s件衣服"%iproduct_g = produce()
print(product_g.__next__()) #要一件衣服
print(product_g.__next__()) #再要一件衣服
print(product_g.__next__()) #再要一件衣服
num = 0
for i in product_g: #要一批衣服,比如5件print(i)num +=1if num == 5:break#到这里我们找工厂拿了8件衣服,我一共让我的生产函数(也就是produce生成器函数)生产2000000件衣服。
#剩下的还有很多衣服,我们可以一直拿,也可以放着等想拿的时候再拿
一个函数可以监听文件的输入:
def tail(filename):f = open(filename, encoding='utf-8')while True:line = f.readline()if line.strip():yield line.rstrip()g = tail('log')
for i in g:if 'python' in i:print('hello', i)