生成器,协成函数
day23
生成器就是一个函数,这个函数内包含有yield这个关键字。
生成器与return有何区别?
return 只能返回一次函数就彻底结束了,而yieId能返回多次。
yield到底干了什么事情:
yield把函数变成迭代器。
总结yield的功能:
-
相当于把__next__ 和 __next__ 方法封装到函数内部
-
与return相比,return只能返回一次,而yield能返回多次
-
函数暂停已经继续运行的状态是通过yield保存的
from collections import Iterator # 生成器就是一个函数,这个函数内包含有yield这个关键字 def test(): print("first") yield 1 # 相当于 return 1 test() # 调用没有任何输出 g = test() # 返回了一个内存地址,是生成器对象 print(g) print(isinstance(g, Iterator)) # 返回True ,说明是迭代器 # next(g) # 得到输出 first print(next(g)) # 同时得到了 输出与返回值 first 和 1 ,如果之前已经next过了 则会报错
输出结果:
<generator object test at 0x000001D79C23A678> True first 1
例子:
def test(): print("hello1") yield 1 print("hello2") print("hello222") yield 2 g = test() print(g.__next__()) # 会输出第一个 yield之前的打印信息 print(g.__next__()) # 会输出第二个和第一个 yield之间的打印信息 print(g.__next__()) # 再执行的时候就会报StopIteration异常了,因为只有2次迭代,已经没有值了 # 既然是迭代器,说明可以进行循环 # while循环 ,执行时请将之前的执行代码注释 while True: try: print(g.__next__()) except StopIteration: break # for循环,执行时请将之前的执行代码注释 for i in g: print(i)
输出结果都是:
hello1 1 hello2 hello222 2
生成器应用:
在shell中我们有tail命令可以实时查看某文件增加的内容,以及可以通过grep只查看含有指定关键字的内容,我们可以使用python来写一个,运用到生成器知识。
import time def tail(file_path): with open(file_path, "r") as f: f.seek(0, 2) # 光标从后开始数 while True: line = f.readline() if not line: time.sleep(0.5) continue else: yield line def grep(keyword, lines): for line in lines: if keyword in line: print(line) g = tail("a.txt") grep("error", g)
建议在linux环境下看效果,运行后使用echo往a.txt文件中新增包含error或不包含error的内容即可看效果。
协成函数:
例子:
# 吃包子例子 def eat(name): print("%s is start to eat baozi." % name) while True: food = yield print("%s is get %s, start eat it." % (name, food)) print("done") g = eat("alex") # 将函数转为生成器 g.__next__() # 触发函数运行,这里是第一次运行 碰到yield暂停,只输出了yield前面的内容 g.send("白菜包子") # send功能和next一样,但是可以将值传给yield ,这里将“白菜包子”传给yield , # 这里是第二次运行从上一次yield停止的地方开始运行,将白菜包子传给food后 继续运行之后的程序 g.send("韭菜包子") # 这里是第三次运行,由于一直在循环中,所以将 韭菜包子又传给yield运行
输出结果:
alex is start to eat baozi. alex is get 白菜包子, start eat it. alex is get 韭菜包子, start eat it.
将吃过的包子保存下来:
# 吃包子例子 def eat(name): print("%s is start to eat baozi." % name) food_list = [] # 设置列表用来存放吃过的 while True: food = yield food_list # 返回list列表 print("%s is get %s, start eat it." % (name, food)) food_list.append(food) # 将已经吃了的加入列表 print("done") g = eat("alex") g.__next__() print(g.send("白菜包子")) # 打印出返回结果,也就是列表内容 print(g.send("韭菜包子")) # 打印出返回结果,也就是列表内容
运行结果:
alex is start to eat baozi. alex is get 白菜包子, start eat it. ['白菜包子'] alex is get 韭菜包子, start eat it. ['白菜包子', '韭菜包子']
e.send 与 next(e) 的区别:
-
如果函数内的yield是表达式形式,那么必须先next(e)
-
二者的共同之处是都可以让函数在上次暂停的位置继续运行,
-
不同的地方在于send在触发下一次代码的执行时,会顺便给yield传一个值
如果函数内的yield是表达式形式使用send时需要先执行next(e),有时会忘记,所以写一个装饰器来处理,但其他函数需要用时也可调用:
# 吃包子例子 def init(func): "这个装饰器用于执行第一next" def wrapper(*args, **kwargs): res = func(*args, **kwargs) next(res) return res return wrapper @init def eat(name): print("%s is start to eat baozi." % name) food_list = [] # 设置列表用来存放吃过的 while True: food = yield food_list # 返回list列表 print("%s is get %s, start eat it." % (name, food)) food_list.append(food) # 将已经吃了的加入列表 print("done") g = eat("alex") # g.__next__() # 有了初始化装饰器就可以不用next了 print(g.send("白菜包子")) # 打印出返回结果,也就是列表内容 print(g.send("韭菜包子")) # 打印出返回结果,也就是列表内容
运行结果:
alex is start to eat baozi. alex is get 白菜包子, start eat it. ['白菜包子'] alex is get 韭菜包子, start eat it. ['白菜包子', '韭菜包子']
共有 0 条评论