一个包含yield关键字的函数就是一个生成器函数。yield可以为我们从函数中返回值,但是yield又不同于return,return的执行意味着程序的结束,调用生成器函数不会得到返回的具体的值,而是得到一个可迭代的对象。每一次获取这个可迭代对象的值,就能推动函数的执行,获取新的返回值。直到函数执行结束。
生成器函数的特点:
1)调用函数之后函数不执行,返回一个生成器;
2)每次调用next方法的时候会取到一个值;
3)直到取完最后一个,在执行next会报错。
- 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' % i
-
-
- g = hello()
-
- for i in g:
- print(i)
背后的原理是:这边生产一个,然后就给你一个打印,边生产边打印,这样就不会一下子在内存中产生大量的数据。不是一次性将所有的数据都生成出来,然后再提供给你。这样,就需要大量的内存空间。数据越存越多,越到后面,程序需要去找内存,非常麻烦。
如果只想打印50个:
- def hello():
- for i in range(2000000):
- yield 'hello %s' % i
-
-
- g = hello()
-
- count = 0
- for i in g:
- count += 1
- print(i)
- if count > 50:
- break
-
- # print(g.__next__())
-
- for i in g:
- count += 1
- print(i)
- if count > 100:
- break
-
这个应该有一个位置记录,迭代的过程中记录下一个位置。下一个for循环就从下一个位置开始获取值。
- l = [1, 2, 3, 4, 5]
-
- for i in l:
- print(i)
- if i == 2:
- break
-
- for i in l:
- print(i)
这个例子里说for循环,是生成了一个迭代器。然后for循环结束之后,第二个for循环,迭代器又从头开始执行。
看一个工厂生产衣服的例子:
- def produce():
- """生产衣服"""
- for i in range(2000000):
- yield "生产了第%s件衣服"%i
-
- product_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 +=1
- if 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)
总结:
从生成器中取值的几个方法:
1)next
2)for
3)数据类型的强制转换:占用内存。
- # 处理文件,用户指定要查找的文件的内容,将文件中包含要查找的内容的每一行都输出到屏幕
-
- def check_file(fn, cont):
- with open(fn, 'r', encoding='utf-8') as f:
- for i in f:
- if cont in i:
- yield i
-
-
- filename = input('filename: ')
- content = input('content: ')
-
- g = check_file(filename, content)
-
- for i in g:
- print(i.strip())