py_19 yield生成器

2020-08-18  本文已影响0人  阿登20
一、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 值

有了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:

# -*- 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:

#注意: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')
上一篇 下一篇

猜你喜欢

热点阅读