yield表达式形式的应用

来源:互联网 发布:南充哪里有淘宝培训 编辑:程序博客网 时间:2024/05/21 17:39

  • 基本格式
  • 如何给yield传值
  • yield表达式形式的应用
    • 栗子一吃货函数
    • 栗子二模拟linux中的命令 grep -rl 查找内容 目录

基本格式

x = yield 把yield的接收值,赋值给x

def foo():    while True:        x = yield   # 函数执行到yield处,暂停,yield后面没有任何返回值,默认返回yield None        print("x's value is ", x) # next执行,打印出x的值,yield 没有接收任何传值,默认接收None,并赋值给x,        # 然后继续执行,返回循环体,又执行到yield处,暂停,返回Noneg = foo()print(next(g))print(next(g))# 运行结果如下:# None# x's value is  None# None

如何给yield传值

带有关键字yield的函数的执行结果就是生成器,假设为g, 调用g.send(value)方法,就可以给yield传值。g.send(None)就是给yieldNone, 相当于没传,也就等同于next(g) 的执行效果。
注意,g.send(value) 的传值方式是基于一个暂停的位置。也就是说生成器函数必须先执行到yield处,暂停在那里,给yield传值,然后执行next(g)。因此,在使用g.send(value) 给yield传值前,要进行初始化:next(g) 或者g.send(None)
我们可以用装饰器来做生成器的初始化:

def init(g_func):   #定义装饰器函数    def wrapper(*args, **kwargs):        g = g_func(*args, **kwargs)        next(g)     # 用next(g)将生成器函数暂停到yield处,以完成初始化        return g    return wrapper@initdef foo():    while True:        x = yield        print("x's value is ",x)g = foo()print(g.send(1))    # 通过装饰器完成了初始化后,就可以直接给yield传值了print(g.send(2))print(g.send(3))# 执行结果如下:# x's value is  1# None# x's value is  2# None# x's value is  3# None

yield表达式形式的应用

栗子一:吃货函数

@init   # 用装饰器来初始化函数def eater(name):    print('{} is ready to eat!'.format(name))    food_list = []    while True:        food = yield food_list  # 在暂停处给yield 传值,yield 得到的值赋值给food(给吃货上菜)。再次执行到yield 暂停,返回food_list        food_list.append(food)        print('{} is eating {}'.format(name, food))e = eater('Seb')    # 执行函数,得到一个生成器print(e.send('meat'))   # 给yield传值并执行函数print(e.send('fish'))   print(e.send('chicken'))   # 执行效果如下:# Seb is ready to eat!# Seb is eating meat# ['meat']# Seb is eating fish# ['meat', 'fish']# Seb is eating chicken# ['meat', 'fish', 'chicken']

上面这个栗子,我们也可以通过一个函数来专门生产yield需要的值,然后从外部传给yield,实现程序的协同工作:

@init   # 用装饰器来初始化函数def eater(name):    print('{} is ready to eat!'.format(name))    food_list = []    while True:        food = yield food_list  # 在暂停处给yield 传值,yield 得到的值赋值给food(给吃货上菜)。再次执行到yield 暂停,返回food_list        food_list.append(food)        print('{} is eating {}'.format(name, food))def make_food(target, n):    for i in range(n):        target.send('food_{}'.format(i))    # target代表一个需要被传值的生成器e = eater('Seb')    # 执行函数,得到一个生成器make_food(e, 3)# 执行效果如下:# Seb is ready to eat!# Seb is eating food_0# Seb is eating food_1# Seb is eating food_2

栗子二:模拟linux中的命令 grep -rl ‘查找内容’ /目录

递归的查找某目录下的文件夹及文件,只要是文件内含有”查找内容“的,打印出文件路径。
分析以上需求,有四个步骤:
1. 递归的找出文件路径,结果给下一步
2. 收到路径,打开文件获取文件,把文件对象给下一步
3. 收到文件,打开文件遍历,取出每一行的内容,给下一步
4. 收到一行内容,判断有没有”查找内容“,有的话,把文件名给下一步
5. 收到文件名,打印
下面通过5个函数来实现,函数之间传递值用target.send(value) ;接收值用var = yield

import os# !!!一个惨痛的教训,定义一个函数时,千万不要和系统内置的名字冲突,否则会各种报错!!!def init(func):    """    装饰器传入一个生成器函数,对其进行初始化    :param g_func:     :return:     """    def wrapper(*args, **kwargs):        g = func(*args, **kwargs)        next(g)        return g    return wrapper@initdef search_file(target):    while True:        dir_path = yield        g = os.walk(dir_path) # os.walk(dir_path)得到的结果是生成器,对这个生成器迭代可以拿到一个个元组。        # 每个元组由(当前目录绝对路径,当前目录下的子目录列表,当前目录下的文件列表)三个元素构成。        # 通过拼接当前目录路径和该目录下的文件,就可以得到每个文件的绝对路径。        for par_dir, _, file_path_list in g: # 迭代,解压元组的元素,当前目录的绝对路径赋值给par_dir,            # 文件路径的集合赋值给file_path_list。不需要的元素丢给 _            for file_path in file_path_list:                file_path = r'%s\%s'% (par_dir, file_path)   # 拼接文件的绝对路径                target.send(file_path)  # 把结果发送给需要被传值的对象@initdef open_file(target):    while True:        file_path = yield        with open(file_path, encoding='utf-8')as f:            target.send((file_path, f))@initdef read_file(target):    while True:        file_path, f = yield        for line in f:            res = target.send((file_path, line))  # 返回值res就是下一个函数的执行的返回值,即tag            if res:                break@initdef grep_file(target, pattern):    tag = False    while True:        file_path, line  = yield tag        tag = False  # 这里保持False状态。否则只要过滤成功一次,tag为True, 那么        if pattern in line:            target.send(file_path)            tag = True  # 匹配出一个文件内包含查找内容后,更改tag的状态,令上一个函数停止遍历该文件,以免发送重复的文件路径。@initdef print_result():    while True:        file_name = yield        print(file_name)dir_path = r'D:\testdir'g = search_file(open_file(read_file(grep_file(print_result(), 'a'))))g.send(dir_path)# 函数执行结果如下:# D:\testdir\testa\a.txt# D:\testdir\testa\aa.txt# !!!一个惨痛的教训,定义一个函数时,千万不要和系统内置的名字冲突,否则会各种报错!!!
原创粉丝点击