先来看一个例子

  1. def foo():
  2. print("starting...")
  3. while True:
  4. res = yield
  5. print("res:",res)
  6. g = foo()
  7. next(g)

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

程序的执行结果为

  1. starting...

使用next(g)触发生成器的执行时,程序会按照正常的顺序从上向下执行,遇到yield,程序就会暂停

并把yield后面所接的值返回

打印next(g)的执行结果

  1. def foo():
  2. print("starting...")
  3. while True:
  4. res = yield
  5. print("res:",res)
  6. g = foo()
  7. print(next(g))

程序执行结果

  1. starting...
  2. None

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

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

  1. def foo():
  2. print("starting...")
  3. while True:
  4. res = yield
  5. print("res:",res)
  6. g = foo()
  7. print(next(g))
  8. print("*"*20)
  9. print(next(g))

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

  1. starting...
  2. None
  3. ********************
  4. res: None
  5. None

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

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

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

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

修改上面的代码

  1. def foo():
  2. print("starting...")
  3. while True:
  4. res = yield
  5. print("res:",res)
  6. g = foo()
  7. next(g)
  8. print(g.send(5))

程序的执行结果为:

  1. starting...
  2. res: 5
  3. None

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

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

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

  1. def foo():
  2. print("starting...")
  3. while True:
  4. res = yield
  5. print("res:",res)
  6. g = foo()
  7. next(g)
  8. print(g.send(5))
  9. print("*"*20)
  10. print(g.send(10))
  11. print("#"*20)
  12. print(g.send(15))

执行程序,得到如下结果

  1. starting...
  2. res: 5
  3. None
  4. ********************
  5. res: 10
  6. None
  7. ####################
  8. res: 15
  9. None

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

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

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

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

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

  1. def foo():
  2. print("starting...")
  3. while True:
  4. res = yield
  5. print("res:",res)
  6. g = foo()
  7. next(g)
  8. print("#"*20)
  9. print(g.send(None))

查看程序的执行结果

  1. starting...
  2. ####################
  3. res: None
  4. None

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

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

代码如下:

  1. import os
  2. def init(func):
  3. def wrapper(*args, **kwargs):
  4. g = func(*args, **kwargs)
  5. next(g)
  6. return g
  7. return wrapper
  8. @init
  9. def get_file_path(target):
  10. """
  11. get file abspath
  12. # 阶段一:递归找文件的绝对路径,把文件的完事路径发送给阶段二
  13. :param target:
  14. :return:
  15. """
  16. while True:
  17. start_path = yield
  18. g = os.walk(start_path)
  19. for parent_dir, _, files in g:
  20. for file in files:
  21. file_path = r"%s\%s" % (parent_dir, file)
  22. target.send(file_path)
  23. @init
  24. def opener(target):
  25. """
  26. get file obj
  27. # 阶段二:收到文件的完整路径,打开文件获取文件对象,把文件对象发送给阶段三
  28. :param target:
  29. :return:
  30. """
  31. while True:
  32. file_path = yield
  33. with open(file_path, encoding='utf-8') as f:
  34. target.send((file_path, f))
  35. @init
  36. def cat_file(target):
  37. """
  38. read file content
  39. # 阶段三:收到文件对象,for循环读取文件的每一行内容,把每一行内容发给阶段四
  40. :param target:
  41. :return:
  42. """
  43. while True:
  44. file_path, f = yield
  45. for line in f:
  46. file_content = target.send((file_path, line))
  47. if file_content:
  48. break
  49. @init
  50. def grep(target, pattern):
  51. """
  52. grep function
  53. # 阶段四:收到文件的一行内容,判断要查找的内容是否在这一行中,如果在,则把文件名发送给阶段五
  54. :param target:
  55. :param pattern:
  56. :return:
  57. """
  58. tag = False
  59. while True:
  60. file_path, line = yield tag
  61. tag = False
  62. if pattern in line:
  63. target.send(file_path)
  64. tag = True
  65. @init
  66. def printer():
  67. """
  68. print file name
  69. # 阶段五:收到文件名,打印结果
  70. :return:
  71. """
  72. while True:
  73. filename = yield
  74. print(filename)
  75. path1 = "/root" # 定义要搜索的路径
  76. path2 = "/etc" # 定义要搜索的路径
  77. g = get_file_path(opener(cat_file(grep(printer(), "root"))))
  78. print(g)
  79. g.send(path1)
  80. g.send(path2)

python函数式编程之yield表达式形式的更多相关文章

  1. Python函数式编程之lambda表达式

    一:匿名函数的定义 lambda parameter_list: expression 二:三元表达式 条件为真时返回的结果 if 条件判断 else 条件为假的时候返回的结果 三:map map(f ...

  2. Python函数式编程之map()

    Python函数式编程之map() Python中map().filter().reduce()这三个都是应用于序列的内置函数. 格式: map(func, seq1[, seq2,…]) 第一个参数 ...

  3. java函数式编程之lambda表达式

    作为比较老牌的面向对象的编程语言java,在对函数式编程的支持上一直不温不火. 认为面向对象式编程就应该纯粹的面向对象,于是经常看到这样的写法:如果你想写一个方法,那么就必须把它放到一个类里面,然后n ...

  4. python 函数式编程之lambda( ), map( ), reduce( ), filter( )

    lambda( ), map( ), reduce( ), filter( ) 1. lambda( )主要用于“行内函数”: f = lambda x : x + 2 #定义函数f(x)=x+2 g ...

  5. Python基础(9)_生成器(yield表达式形式)、面向过程编程

    一.yield表达式形式 1 #装饰器,初始化含yield表达式的生成器 def init(func): def wrapper(*args,**kwargs): g=func(*args,**kwa ...

  6. Python 多进程编程之multiprocessing--Pool

    Python 多进程编程之multiprocessing--Pool ----当需要创建的子进程数量不多的时候,可以直接利用multiprocessing 中的Process 动态生成多个进程, -- ...

  7. Python 多进程编程之multiprocessing--Process

    Python 多进程编程之multiprocessing 1,Process 跨平台的进程创建模块(multiprocessing), 支持跨平台:windowx/linux 创建和启动      创 ...

  8. python并发编程之Queue线程、进程、协程通信(五)

    单线程.多线程之间.进程之间.协程之间很多时候需要协同完成工作,这个时候它们需要进行通讯.或者说为了解耦,普遍采用Queue,生产消费模式. 系列文章 python并发编程之threading线程(一 ...

  9. python并发编程之gevent协程(四)

    协程的含义就不再提,在py2和py3的早期版本中,python协程的主流实现方法是使用gevent模块.由于协程对于操作系统是无感知的,所以其切换需要程序员自己去完成. 系列文章 python并发编程 ...

随机推荐

  1. spider RPC更新至2.0.0-RELEASE

    spider使用java语言开发,使用Spring作为IoC容器,采用TCP/IP协议,在此基础上,结合SaaS金融交易系统的特性进行针对性和重点设计,以更加灵活和高效的满足金融交易系统多租户.高可用 ...

  2. Linux中select poll和epoll的区别

    在Linux Socket服务器短编程时,为了处理大量客户的连接请求,需要使用非阻塞I/O和复用,select.poll和epoll是Linux API提供的I/O复用方式,自从Linux 2.6中加 ...

  3. CSS3之background-clip

    1.属性简介 background-clip:padding|border|content|text|!important 2.兼容性 (1)IE6.7.8不兼容 (2)火狐3.0以上兼容 (3)Ch ...

  4. AJAX跨域问题总结

    跨域是什么? 首先说下同源,同源策略是浏览器的一种安全策略,所谓同源是指,域名,协议,端口完全相同.而跨域就是不同源 ! 能够进行跨域的请求 一般a,img,link[rel=stylesheet], ...

  5. WPF自学入门(二)WPF-XAML布局控件

    上一篇介绍了xaml基本知识,我们已经知道了WPF简单的语法.那么接下来,我们要认识一下WPF的布局容器.布局容器可以使控件按照分类显示,我们一起来看看WPF里面可以使用哪些布局容器用来布局. 在WP ...

  6. ASP.NET Core 2.0: 二. 开发环境

    macOS:Install Visual Studio for Mac 系统要求: macOS 10.12 Sierra 及更高版本 其他要求: 可能会要求安装xcode或android相关环境, 详 ...

  7. C# SerialPort自定义串口DCB

    C# SerialPort自定义串口DCBChange DCB fields from SerialPort instance CPS:中文DCB结构详解表 译自Change DCB fields f ...

  8. 【视频编解码·学习笔记】10. 序列参数集(SPS)介绍

    一.SPS 相关概念: SPS即 "Sequence Paramater Set",又称作序列参数集. SPS中保存了一组编码视频序列(Coded video sequence)的 ...

  9. mysql5.6版本备份报错

    MySQL5.6版本备份报错,密码不安全 [root@centos199 mysql]# mysqldump -uroot -ppassword cz-office > mysql38.sqlW ...

  10. mailgun 发邮件示例代码Python版

    1 首先到mailgun官网注册账号,并激活账号 点击domains,进入默认的域名,最底下那个sandbox域名就是默认的测试域名 如果自己有域名,也可以添加自己的域名测试,具体参考:ssr pan ...