python函数式编程学习之yield表达式形式详解


Posted in Python onMarch 25, 2018

前言

yield的英文单词意思是生产,刚接触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)

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Python 相关文章推荐
多版本Python共存的配置方法
May 22 Python
一文总结学习Python的14张思维导图
Oct 17 Python
Python实现七彩蟒蛇绘制实例代码
Jan 16 Python
利用Python代码实现数据可视化的5种方法详解
Mar 25 Python
python3库numpy数组属性的查看方法
Apr 17 Python
对Tensorflow中权值和feature map的可视化详解
Jun 14 Python
python-itchat 统计微信群、好友数量,及原始消息数据的实例
Feb 21 Python
详解利用OpenCV提取图像中的矩形区域(PPT屏幕等)
Jul 01 Python
Django 框架模型操作入门教程
Nov 05 Python
pytorch绘制并显示loss曲线和acc曲线,LeNet5识别图像准确率
Jan 02 Python
python 实现学生信息管理系统的示例
Nov 28 Python
python中编写函数并调用的知识点总结
Jan 13 Python
Python实现简单求解给定整数的质因数算法示例
Mar 25 #Python
python实现隐马尔科夫模型HMM
Mar 25 #Python
Python实现的寻找前5个默尼森数算法示例
Mar 25 #Python
Python实现修改文件内容的方法分析
Mar 25 #Python
利用python为运维人员写一个监控脚本
Mar 25 #Python
python实现数据写入excel表格
Mar 25 #Python
使用requests库制作Python爬虫
Mar 25 #Python
You might like
PHP的FTP学习(二)
2006/10/09 PHP
PHP修改session_id示例代码
2014/01/08 PHP
PHP单例模式是什么 php实现单例模式的方法
2016/05/14 PHP
基于jQuery的弹出消息插件 DivAlert之旅(一)
2010/04/01 Javascript
将input file的选择的文件清空的两种解决方案
2013/10/21 Javascript
Javascript 按位与运算符 (&)使用介绍
2014/02/04 Javascript
Google Maps API地图应用示例分享
2014/10/23 Javascript
JavaScript中setUTCFullYear()方法的使用简介
2015/06/12 Javascript
Javascript BOM学习小结(六)
2015/11/26 Javascript
浅谈JavaScript事件绑定的常用方法及其优缺点分析
2016/11/01 Javascript
JavaScript中无法通过div.style.left获取值的解决方法
2017/02/19 Javascript
Vue-cli proxyTable 解决开发环境的跨域问题详解
2017/05/18 Javascript
Node.js+jade抓取博客所有文章生成静态html文件的实例
2017/09/19 Javascript
JavaScript实现多重继承的方法分析
2018/01/09 Javascript
vue使用Element组件时v-for循环里的表单项验证方法
2018/06/28 Javascript
Vue2.0生命周期的理解
2018/08/20 Javascript
angular.js实现列表orderby排序的方法
2018/10/02 Javascript
vue组件实践之可搜索下拉框功能
2018/11/25 Javascript
解决Layui当中的导航条动态添加后渲染失败的问题
2019/09/25 Javascript
vue改变循环遍历后的数据实例
2019/11/07 Javascript
使用python对excle和json互相转换的示例
2018/10/23 Python
python实现石头剪刀布程序
2021/01/20 Python
基于python历史天气采集的分析
2019/02/14 Python
softmax及python实现过程解析
2019/09/30 Python
PIL包中Image模块的convert()函数的具体使用
2020/02/26 Python
Pytest如何使用skip跳过执行测试
2020/08/13 Python
Pycharm 如何一键加引号的方法步骤
2021/02/05 Python
韩国三星旗下的一家超市连锁店:Home Plus
2016/07/30 全球购物
国贸专业的职业规划范文
2014/01/23 职场文书
十佳大学生事迹材料
2014/01/29 职场文书
幼儿教育感言
2014/02/05 职场文书
反腐倡廉演讲稿
2014/05/22 职场文书
2015年端午节活动总结
2015/02/11 职场文书
2015年领导班子工作总结
2015/05/23 职场文书
探讨Java中的深浅拷贝问题
2021/06/26 Java/Android
vue-cil之axios的二次封装与proxy反向代理使用说明
2022/04/07 Vue.js