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中的整数求平均并排序
Sep 12 Python
Python实现将文本生成二维码的方法示例
Jul 18 Python
python+POP3实现批量下载邮件附件
Jun 19 Python
Python使用matplotlib绘制三维图形示例
Aug 25 Python
Python实现多级目录压缩与解压文件的方法
Sep 01 Python
对python csv模块配置分隔符和引用符详解
Dec 12 Python
深入解析Python小白学习【操作列表】
Mar 23 Python
python输出决策树图形的例子
Aug 09 Python
Python计算公交发车时间的完整代码
Feb 12 Python
谈谈python垃圾回收机制
Sep 27 Python
Python下使用Trackbar实现绘图板
Oct 27 Python
python 爬取百度文库并下载(免费文章限定)
Dec 04 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
解析web文件操作常见安全漏洞(目录、文件名检测漏洞)
2013/06/29 PHP
ThinkPHP自动完成中使用函数与回调方法实例
2014/11/29 PHP
PHP获取文件相对路径的方法
2015/02/26 PHP
PHP aes (ecb)解密后乱码问题
2015/06/22 PHP
CodeIgniter框架基本增删改查操作示例
2017/03/23 PHP
Yii框架where查询用法实例分析
2019/10/22 PHP
Gambit vs CL BO3 第一场 2.13
2021/03/10 DOTA
javascript下阻止表单重复提交、防刷新、防后退
2007/08/17 Javascript
浅析LigerUi开发中谨慎载入common.css文件
2013/07/09 Javascript
javascript自然分类法算法实现代码
2013/10/11 Javascript
javascript实现倒计时跳转页面
2016/01/17 Javascript
jQuery实现点击查看大图并以弹框的形式居中
2016/08/08 Javascript
JavaScript实现清空(重置)文件类型INPUT元素值的方法
2016/11/17 Javascript
详解vue 中使用 AJAX获取数据的方法
2017/01/18 Javascript
vue子父组件通信的实现代码
2017/07/09 Javascript
深入探究node之Transform
2017/07/20 Javascript
深入掌握 react的 setState的工作机制
2017/09/27 Javascript
微信小程序实现页面分享onShareAppMessage
2019/08/12 Javascript
Python实现字典依据value排序
2016/02/24 Python
简单谈谈Python中的json与pickle
2017/07/19 Python
python操作mysql代码总结
2018/06/01 Python
python实现修改固定模式的字符串内容操作示例
2019/12/30 Python
Pycharm和Idea支持的vim插件的方法
2020/02/21 Python
解决django无法访问本地static文件(js,css,img)网页里js,cs都加载不了
2020/04/07 Python
python递归函数用法详解
2020/10/26 Python
用python获取txt文件中关键字的数量
2020/12/24 Python
如何查看python关键字
2021/01/17 Python
Python爬虫实现selenium处理iframe作用域问题
2021/01/27 Python
NBA欧洲商店(法国):NBA Europe Store FR
2016/10/19 全球购物
通往英国高街的商店橱窗:Down Your High Street
2020/07/19 全球购物
2014年技术部工作总结
2014/12/12 职场文书
十岁生日答谢词
2015/01/05 职场文书
英文感谢信格式
2015/01/21 职场文书
还在手动盖楼抽奖?教你用Python实现自动评论盖楼抽奖(一)
2021/06/07 Python
vue+element ui实现锚点定位
2021/06/29 Vue.js
idea以任意顺序debug多线程程序的具体用法
2021/08/30 Java/Android