修改程序,多次调用__next__方法,查看程序的执行结果
def foo(): print("first...") yield print("second...") yield print("third...") g=foo() print(g) print(g.__iter__()) g.__next__() print('*'*30) g.__next__() print('#'*30) g.__next__()程序执行结果
<generator object foo at 0x0000000001DF2BF8> <generator object foo at 0x0000000001DF2BF8> first... ****************************** second... ############################## third... Traceback (most recent call last): File "E:/py_code/test.py", line 28, in <module> g.__next__() StopIteration从上面程序的执行结果可以看出,每调用一次生成器的__next__方法,会得到一个返回值,就相当于从迭代器中取一个值。
如果程序在执行过程中,没有得到返回值,这就说明迭代器的最后一个值已经被遍历完成了,所以此时再调用__next__方法,程序就会抛出异常
9.生成器的for循环遍历在前面的学习中已经知道,生成器本质上就是一个迭代器。既然是迭代器,那么当然可以使用for循环来遍历生成器
修改上面的例子,使用for循环来遍历生成器
def foo(): print("first...") yield 1 print("second...") yield 2 print("third...") g=foo() for i in g: print(i) print("*"*30)查看程序的执行结果
first... 1 ****************************** second... 2 ****************************** third...在上面的例子里,每执行一次for循环,就相当于是执行一次g.__next__方法,yield会返回其后所接的数字,所以for循环前两次的执行结果都是print函数和yield后接的数字
for循环执行到第三次的时候,执行完print函数,程序会抛出StopIteration异常,但是StopIteration的异常会被for循环捕捉到,所以for循环执行第三次只执行了print语句
10.总结:yield关键字的功能:
与return的功能类似,都可以返回值,但不一样的地方在于一个函数中可以多次调用yield来返回值 为函数封装好了`__iter__方法`和`__next__方法`,把函数的执行结果变成了迭代器 `遵循迭代器的取值方式(obj.__next__())`,触发的函数的执行,函数暂停与再继续都由yield保存 11.示例:使用yield模拟linux中的命令:tail -f | grep 'error' | grep '404'代码如下:
import time def tail(file_path, encoding='utf-8'): with open(file_path, encoding=encoding) as f: f.seek(0, 2) while True: line = f.readline() if line: yield line else: time.sleep(0.5) def grep(lines, pattern): for line in lines: if pattern in line: yield line g1 = tail('a.txt') g2 = grep(g1, 'error') g3 = grep(g2, '404') for i in g3: print(i)