生成器,协成函数
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 条评论