一、yield:函数体内有关键字yield,返回值是生成器
1.yield 能返回多次值
2.next(生成器)触发生成器所对应函数代码的执行
3.生成器是迭代器就可以用for迭代循环和while循环
二、二 yield表达式应用
2.1 yield后面不跟值
2.2 yield 后面有返回值
2.3 表达式形式的yield也可以用于返回多次值,即变量名=yield 值 的形式
2.4装饰器来为所有表达式形式yield对应生成器的初始化操作
2.5 yield案例1:
2.6 yield案例2:
image.png
一、yield:函数体内有关键字yield,返回值是生成器
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator
在函数内一旦存在yield关键字,调用函数并不会执行函数体代码,会返回一个生成器对象,生成器即为自定义迭代器.
1.yield 能返回多次值
def func():
print("第1次")
yield 1
print("第2次")
yield 2
print("第3次")
yield 3
f = func() # 我调用函数断点放在这里,并没有执行函数体代码。返回值是一个 生成器generator
print(f) # <generator object func at 0x0000000000521ED0>
2.next(生成器)触发生成器所对应函数代码的执行
next(生成器)会触发函数体代码的运行,然后遇到yield停下来,将yield后的值
当做本次调用的结果返回
print(next(f))
print(next(f))
print(next(f))
# print(next(f)) # 没有返回值了抛出异常:StopIteration
# f.__next__() #
# print(f.__next__())
3.生成器是迭代器就可以用for迭代循环和while循环
for循环演示
def func():
print("第1次")
yield 1
print("第2次")
yield 2
print("第3次")
yield 3
fun()调用它是一个生成器,i去接受next(func()) yield返回值
for i in func():
print(i)
但是用for循环调用generator时,发现拿不到generator的return语句的返回值。
如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中
f = func()
while True:
try:
a= next(f) # 调用生成器f的函数体代码将yield返回值赋值给 a
print(a,type(a))
except StopIteration as e:
res = e.value # 返回值包含在StopIteration的value中
print(res)
break
def my_range(start, stop, step):
print("开始")
while start < stop:
yield start
start += step
g = my_range(1,5,2)
print(next(g)) # 第一次执行到yield 将返回值start返回,然后代码停在这一行
print(next(g)) # 第2次 代码从上一次yield 下一行代码开始执行---执行while循环到yield,又将返回值返回,代码停在yield这一行
# print(next(g)) # 第3次 重复第2的步骤,但是start这时候等于5,不满足循环。于是跳出循环,yield这时候没有执行到这一行代码。抛出异常StopIteration,无yield抛出异常。
for n in my_range(1,7,2):
print(n)
二 yield表达式应用
变量名=yield 值
- g = func() 函数调用得到一个生成器
- g.send(None) or next(g) 事先”初始化”一次,让函数挂起在 变量名=yield,等待调用g.send()方法为其传值
- g.send(value)
实现过程: 将值传给yild赋值给变量,然后运行yield下面的代码直到暂停到下一次yield处。将 返回值 赋值给 res
- g.close() 关闭后无法seed传值
有了yield的好处:
普通函数调用,生成名称空间--然后销毁,再调再销毁。
有了yield,调用函数得到一个生成器。send传一个值,函数会挂起在yield代码处,函数还没结束,函数暂停。
等其他程序运行,传一个值给seed,这时候调用g.seed(value)。函数体代码会继续运行直到下次到yield处暂停,函数继续挂起。
2.1 yield后面不跟值
# 可以拿到函数的生成器对象持续为函数体send值
# 变量名=yield 值
def dog(name):
print(f'嫖哥{name}准备吃东西啦')
while True:
print(1)
food=yield
print(f'嫖哥{name}吃了{food}')
g = dog("维维") # 函数调用 得到一个生成器
# next(g) # 需要事先”初始化”一次,让函数挂起在food=yield,等待调用g.send()方法为其传值
# send 给yield赋值,给food.food拿到的是send传的值。不是yield的返回值
# send有 next的功能
g.send(None) # 等同于 next(g) 需要事先”初始化”一次,让函数挂起在food=yield,等待调用g.send()方法为其传值
g.send("面包")
g.send("螃蟹")
# 理解
# 普通函数调用,生成名称空间--然后销毁,再调再销毁。
# 有了yield,调用函数得到一个生成器。send传一个值,函数会挂起在yield代码处,函数还没结束,函数暂停。
# 等其他程序运行,传一个值给seed,这时候调用g.seed(value)。函数体代码会继续运行直到下次到yield暂停。
g.close()
g.send("鲍鱼") # 关闭之后无法传值
# g.send("1","2")
g.send(["1","2"])
2.2 yield 后面有返回值
send将值传给yield赋值给变量名,然后运行yield下面的代码直到暂停到下一次yield处。将返回值赋值给res
def dog(name):
print(f'嫖哥{name}准备吃东西啦')
while True:
print(1)
food=yield 111
print(f'嫖哥{name}吃了{food}')
print("-".center(50,"-"))
g = dog("向佳")
res = g.send(None)
print(res)
res=g.send("面包") # 将值传给yield赋值给food,然后运行yield下面的代码直到暂停到下一次yield处。将返回值111赋值给res
print(res)
res=g.send("螃蟹")
print(res)
结果:
--------------------------------------------------
嫖哥向佳准备吃东西啦
1
111
嫖哥向佳吃了面包
1
111
嫖哥向佳吃了螃蟹
1
111
2.3 表达式形式的yield也可以用于返回多次值,即变量名=yield 值 的形式,如下
def dog(name):
food_list = []
print(f'嫖哥{name}准备吃东西啦')
while True:
print(1)
food=yield food_list
food_list.append(food)
print(f'嫖哥{name}吃了{food}')
g = dog("向佳")
res = g.send(None)
print(res)
res=g.send("面包") # 将值传给yield赋值给food,然后运行yield下面的代码直到暂停到下一次yield处。将返回值 赋值给res
print(res)
res=g.send("螃蟹")
print(res)
"""
嫖哥向佳准备吃东西啦
1
[]
嫖哥向佳吃了面包
1
['面包']
嫖哥向佳吃了螃蟹
1
['面包', '螃蟹']
"""
【2.3代码实现过程画图理解如下】
image.png
2.4装饰器来为所有表达式形式yield对应生成器的初始化操作
# 2.4我们可以编写装饰器来完成为所有表达式形式yield对应生成器的初始化操作,如下
def init(func):
def wrapper(*args, **kwargs):
g = func(*args,**kwargs) # 如果func里面有yield关键字 调用函数不会执行函数体代码
next(g) # 会执行func函数体代码到yield处暂停。这里主要是初始化一下
return g
return wrapper
@init
def dog(name):
food_list = []
print(f'嫖哥{name}准备吃东西啦')
while True:
print(1)
food=yield food_list
food_list.append(food)
print(f'嫖哥{name}吃了{food}')
g = dog("徐州") # dog的内存地址是wrapper的内存地址。g是生成器---dog()函数
print("========================")
print(g) # <generator object dog at 0x000000000290C840>
res=g.send("火腿") # 装饰器init已经初始化过,所以这里代码刚开始是停留在yield处的。
print(res)
res=g.send("羊肉串")
print(res)
2.5 yield案例1:
- 模拟管道,实现功能:tail -f access.log | grep '404'
# -*- coding: utf-8 -*-
"""
===========================
# @Time : 2020/8/18 12:52
# @File : 13 yield tail_grep案例.py
# @Author: adeng
# @Date : 2020/8/18
============================
"""
# 模拟管道,实现功能:tail -f access.log | grep '404'
import time
def tail(filepath):
with open(filepath,'rb') as f:
f.seek(0,2)
while True:
line=f.readline()
if line:
yield line
else:
time.sleep(0.2)
def grep(pattern,lines):
for line in lines:
line=line.decode('utf-8')
if pattern in line:
yield line
for line in grep('404',tail('access.log')):
print(line,end='')
#测试
with open('access.log','a',encoding='utf-8') as f:
f.write('出错啦404\n')
2.6yield案例2:
- 实现功能:grep -rl 'python' /etc 递归查找etc目录下包含 python字符的所有文件
#注意:target.send(...)在拿到target的返回值后才算执行结束
import os
def init(func):
def wrapper(*args,**kwargs):
g=func(*args,**kwargs)
next(g)
return g
return wrapper
@init
def search(target):
while True:
filepath=yield
g=os.walk(filepath)
for dirname,_,files in g:
for file in files:
abs_path=r'%s\%s' %(dirname,file)
target.send(abs_path)
@init
def opener(target):
while True:
abs_path=yield
with open(abs_path,'rb') as f:
target.send((f,abs_path))
@init
def cat(target):
while True:
f,abs_path=yield
for line in f:
res=target.send((line,abs_path))
if res:
break
@init
def grep(pattern,target):
tag=False
while True:
line,abs_path=yield tag
tag=False
if pattern.encode('utf-8') in line:
target.send(abs_path)
tag=True
@init
def printer():
while True:
abs_path=yield
print(abs_path)
g=search(opener(cat(grep('你好',printer()))))
# g.send(r'E:\CMS\aaa\db')
g=search(opener(cat(grep('python',printer()))))
g.send(r'E:\CMS\aaa\db')








网友评论