• python函数式编程之yield表达式形式


    先来看一个例子

    def foo():
        print("starting...")
        while True:
            res = yield
            print("res:",res)
    
    g = foo()
    next(g)
    

    在上面的例子里,因为foo函数中有yield关键字,所以foo()函数的执行结果g是一个生成器,此时可以使用next(g)或者g.__next__()方法触发生成器的执行

    程序的执行结果为

    starting...
    

    使用next(g)触发生成器的执行时,程序会按照正常的顺序从上向下执行,遇到yield,程序就会暂停
    并把yield后面所接的值返回

    打印next(g)的执行结果

    def foo():
        print("starting...")
        while True:
            res = yield
            print("res:",res)
    
    g = foo()
    print(next(g))
    

    程序执行结果

    starting...
    None
    

    在上面的例子里,执行一次next(g)方法,程序暂停在yield那一行,此时再次调用next(g),程序会从yield语句那一行继续向下运行

    修改上面的代码,多调用几次next方法,并打印next方法的返回结果

    def foo():
        print("starting...")
        while True:
            res = yield
            print("res:",res)
    
    g = foo()
    print(next(g))
    print("*"*20)
    print(next(g))
    

    上面这段代码的执行结果为

    starting...
    None
    ********************
    res: None
    None
    

    可以看到,程序确实按猜想的步骤运行,但是上面的程序也有一个很明显的缺点:那就是上面的代码没有任何的实际意义:res的值永远为None

    在实际的开发中,使用yield表达式形式的目的是yield可以得到一个值,然后yield把这个值赋值给某个变量,这样才有实际意义

    那应该怎么操作才能为res变量赋一个值呢??那就是调用生成器自身的send方法

    send方法可以触发一次生成器执行,同时还可以把send方法的参数传递给yield
    

    修改上面的代码

    def foo():
        print("starting...")
        while True:
            res = yield
            print("res:",res)
    
    g = foo()
    next(g)
    print(g.send(5))
    

    程序的执行结果为:

    starting...
    res: 5
    None
    

    来分析一下上面的代码的执行过程 :

    1.程序开始执行以后,因为foo函数中有yield关键字,所以foo函数并不会真的执行,而是先得到一个生成器g.
    2.直到调用next方法,foo函数正式开始执行,先执行foo函数中的print方法,然后进入while循环
    3.程序遇到yield关键字,程序暂停,此时next(g)语句执行完成
    4.程序执行g.send(5),程序会从yield关键字那一行继续向下运行,send会把5这个值传递给yield
    5.yield接收到send方法传递过来的值,然后由yield赋值给res变量
    6.由于send方法中包含next()方法,所以程序会继续向下运行执行print方法,然后再次进入while循环
    7.程序执行再次遇到yield关键字,yield会返回后面的值,由于yield后面没有接任何参数,所以yield会返回None,程序再次暂停,直到再次调用next方法或send方法
    

    修改代码,多次调用send方法

    def foo():
        print("starting...")
        while True:
            res = yield
            print("res:",res)
    
    g = foo()
    next(g)
    print(g.send(5))
    print("*"*20)
    print(g.send(10))
    print("#"*20)
    print(g.send(15))
    

    执行程序,得到如下结果

    starting...
    res: 5
    None
    ********************
    res: 10
    None
    ####################
    res: 15
    None
    

    可以看到,上面代码的执行过程如同上面的分析的执行过程一样运行

    在上面的例子里,如果调用send方法时,传递的参数为None,得到的结果会是怎么样的呢??

    从上面的分析中,可以知道:

    如果`g.send()`方法发送给yield关键字的参数为None,则yield关键字传递给res变量的值就为None
    由于yield后面本来没有接任何值,所以yield返回的值默认也为None,所以程序执行结果会得到两个None
    

    修改代码,验证上面的猜想

    def foo():
        print("starting...")
        while True:
            res = yield
            print("res:",res)
    
    g = foo()
    next(g)
    print("#"*20)
    print(g.send(None))
    

    查看程序的执行结果

    starting...
    ####################
    res: None
    None
    

    从程序的执行结果可以看出,如果调用生成器的send方法时,传递的参数为None,则程序执行的结果将会是两个None

    使用yield表达式形式实现linux系统中的"grep -rl root /etc"命令

    代码如下:

    import os
    
    def init(func):
        def wrapper(*args, **kwargs):
            g = func(*args, **kwargs)
            next(g)
            return g
        return wrapper
    
    @init
    def get_file_path(target):
        """
        get file abspath
        # 阶段一:递归找文件的绝对路径,把文件的完事路径发送给阶段二
        :param target:
        :return:
        """
        while True:
            start_path = yield
            g = os.walk(start_path)
            for parent_dir, _, files in g:
                for file in files:
                    file_path = r"%s\%s" % (parent_dir, file)
                    target.send(file_path)
    
    @init
    def opener(target):
        """
        get file obj
        # 阶段二:收到文件的完整路径,打开文件获取文件对象,把文件对象发送给阶段三
        :param target:
        :return:
        """
        while True:
            file_path = yield
            with open(file_path, encoding='utf-8') as f:
                target.send((file_path, f))
    
    @init
    def cat_file(target):
        """
        read file content
        # 阶段三:收到文件对象,for循环读取文件的每一行内容,把每一行内容发给阶段四
        :param target:
        :return:
        """
        while True:
            file_path, f = yield
            for line in f:
                file_content = target.send((file_path, line))
                if file_content:
                    break
    
    @init
    def grep(target, pattern):
        """
        grep function
        # 阶段四:收到文件的一行内容,判断要查找的内容是否在这一行中,如果在,则把文件名发送给阶段五
        :param target:
        :param pattern:
        :return:
        """
        tag = False
        while True:
            file_path, line = yield tag
            tag = False
            if pattern in line:
                target.send(file_path)
                tag = True
    
    @init
    def printer():
        """
        print file name
        # 阶段五:收到文件名,打印结果
        :return:
        """
        while True:
            filename = yield
            print(filename)
    
    path1 = "/root"         # 定义要搜索的路径
    path2 = "/etc"          # 定义要搜索的路径
    
    g = get_file_path(opener(cat_file(grep(printer(), "root"))))
    print(g)
    g.send(path1)
    g.send(path2)
  • 相关阅读:
    金融资产的票面利率与实际利率
    对于确定承诺的外汇风险,既属于公允价值套期,又属于现金流量套期,怎么区分呢?
    套期工具(公允价值套期与现金流量套期)
    R语言使用 LOWESS技术图分析逻辑回归中的函数形式
    R语言ROC曲线下的面积
    R语言Poisson回归的拟合优度检验
    R语言在逻辑回归中求R square R方
    R平方/相关性取决于预测变量的方差
    stata具有异方差误差的区间回归
    R语言用于线性回归的稳健方差估计
  • 原文地址:https://www.cnblogs.com/renpingsheng/p/8635777.html
Copyright © 2020-2023  润新知