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中List的sort方法指南
Sep 01 Python
复习Python中的字符串知识点
Apr 14 Python
python中urllib.unquote乱码的原因与解决方法
Apr 24 Python
python爬虫系列Selenium定向爬取虎扑篮球图片详解
Nov 15 Python
Python中使用支持向量机SVM实践
Dec 27 Python
python使用xpath中遇到:到底是什么?
Jan 04 Python
Django添加feeds功能的示例
Aug 07 Python
python 处理数字,把大于上限的数字置零实现方法
Jan 28 Python
Python内置类型性能分析过程实例
Jan 29 Python
如何通过python实现全排列
Feb 11 Python
Python实现Kerberos用户的增删改查操作
Dec 14 Python
python创建字典及相关管理操作
Apr 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实现的网站目录扫描索引工具
2016/09/08 PHP
php实现文章置顶功能的方法
2016/10/20 PHP
PHP实现原生态图片上传封装类方法
2016/11/08 PHP
php导出csv文件,可导出前导0实例代码
2016/11/16 PHP
JavaScript实现简单图片滚动附源码下载
2014/06/17 Javascript
超赞的jQuery图片滑块动画特效代码汇总
2016/01/25 Javascript
JS实现点击登录弹出窗口同时背景色渐变动画效果
2016/03/25 Javascript
javascript 内置对象及常见API详细介绍
2016/11/01 Javascript
Javascript使用function创建类的两种方法(推荐)
2016/11/19 Javascript
详解有关easyUI的拖动操作中droppable,draggable用法例子
2017/06/03 Javascript
webpack实现热加载自动刷新的方法
2017/07/30 Javascript
ES6模块化的import和export用法方法总结
2017/08/08 Javascript
微信小程序列表渲染功能之列表下拉刷新及上拉加载的实现方法分析
2017/11/27 Javascript
聊聊鉴权那些事(推荐)
2019/08/22 Javascript
在Vue中使用Echarts可视化库的完整步骤记录
2020/11/18 Vue.js
[04:52]第二届DOTA2亚洲邀请赛主赛事第一天比赛集锦:OG娜迦海妖放大配合谜团大中3人
2017/04/02 DOTA
用实例分析Python中method的参数传递过程
2015/04/02 Python
Python运算符重载用法实例
2015/05/28 Python
Django 2.0版本的新特性抢先看!
2018/01/05 Python
Python去除、替换字符串空格的处理方法
2018/04/01 Python
Python将一个CSV文件里的数据追加到另一个CSV文件的方法
2018/07/04 Python
详解python列表生成式和列表生成式器区别
2019/03/27 Python
Python3+Requests+Excel完整接口自动化测试框架的实现
2019/10/11 Python
Python 闭包,函数分隔作用域,nonlocal声明非局部变量操作示例
2019/10/14 Python
python实现根据给定坐标点生成多边形mask的例子
2020/02/18 Python
celery在python爬虫中定时操作实例讲解
2020/11/27 Python
HTML5之SVG 2D入门10—滤镜的定义及使用
2013/01/30 HTML / CSS
美国当红的名品折扣网:Gilt Groupe
2016/08/15 全球购物
英国电视和家用电器购物网站:rlrdistribution.co.uk
2018/11/20 全球购物
大学生毕业自我鉴定范文
2013/11/03 职场文书
学习雷锋活动总结
2014/04/29 职场文书
预备党员转正考核材料
2014/06/03 职场文书
食品工程专业求职信
2014/06/15 职场文书
2015年司机年终工作总结
2015/05/14 职场文书
Python中tkinter的用户登录管理的实现
2021/04/22 Python
使用feign服务调用添加Header参数
2021/06/23 Java/Android