前言
yield的英文单词意思是生产,刚接触Python的时候感到非常困惑,一直没弄明白yield的用法。最近又重新学习了下,所以整理了下面这篇文章,供自己和大家学习参考,下面话不多说了,来一起看看详细的介绍吧。
先来看一个例子
1
2
3
4
5
6
7
8
|
def foo():
print ( "starting..." )
while True :
res = yield
print ( "res:" ,res)
g = foo()
next (g)
|
在上面的例子里,因为foo函数中有yield关键字,所以foo()函数的执行结果g是一个生成器,此时可以使用next(g)或者g.__next__()方法触发生成器的执行
程序的执行结果为
1
|
starting...
|
使用next(g)触发生成器的执行时,程序会按照正常的顺序从上向下执行,遇到yield,程序就会暂停
并把yield后面所接的值返回
打印next(g)的执行结果
1
2
3
4
5
6
7
8
|
def foo():
print ( "starting..." )
while True :
res = yield
print ( "res:" ,res)
g = foo()
print ( next (g))
|
程序执行结果
1
2
|
starting...
None
|
在上面的例子里,执行一次next(g)方法,程序暂停在yield那一行,此时再次调用next(g),程序会从yield语句那一行继续向下运行
修改上面的代码,多调用几次next方法,并打印next方法的返回结果
1
2
3
4
5
6
7
8
9
|
def foo():
print ( "starting..." )
while True :
res = yield
print ( "res:" ,res)
g = foo()
print ( next (g))
print ( "*" * 20 )
print ( next (g))
|
上面这段代码的执行结果为
1
2
3
4
5
|
starting...
None
********************
res: None
None
|
可以看到,程序确实按猜想的步骤运行,但是上面的程序也有一个很明显的缺点:那就是上面的代码没有任何的实际意义:res的值永远为None
在实际的开发中,使用yield表达式形式的目的是yield可以得到一个值,然后yield把这个值赋值给某个变量,这样才有实际意义
那应该怎么操作才能为res变量赋一个值呢??那就是调用生成器自身的send方法
send方法可以触发一次生成器执行,同时还可以把send方法的参数传递给yield
修改上面的代码
1
2
3
4
5
6
7
8
|
def foo():
print ( "starting..." )
while True :
res = yield
print ( "res:" ,res)
g = foo()
next (g)
print (g.send( 5 ))
|
程序的执行结果为:
1
2
3
|
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方法
1
2
3
4
5
6
7
8
9
10
11
12
|
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 ))
|
执行程序,得到如下结果
1
2
3
4
5
6
7
8
9
|
starting...
res: 5
None
********************
res: 10
None
####################
res: 15
None
|
可以看到,上面代码的执行过程如同上面的分析的执行过程一样运行
在上面的例子里,如果调用send方法时,传递的参数为None,得到的结果会是怎么样的呢??
从上面的分析中,可以知道:
如果`g.send()`方法发送给yield关键字的参数为None,则yield关键字传递给res变量的值就为None
由于yield后面本来没有接任何值,所以yield返回的值默认也为None,所以程序执行结果会得到两个None
修改代码,验证上面的猜想
1
2
3
4
5
6
7
8
9
|
def foo():
print ( "starting..." )
while True :
res = yield
print ( "res:" ,res)
g = foo()
next (g)
print ( "#" * 20 )
print (g.send( None ))
|
查看程序的执行结果
1
2
3
4
|
starting...
####################
res: None
None
|
从程序的执行结果可以看出,如果调用生成器的send方法时,传递的参数为None,则程序执行的结果将会是两个None
使用yield表达式形式实现linux系统中的"grep -rl root /etc"命令
代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
|
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)
|
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对服务器之家的支持。
原文链接:https://www.cnblogs.com/renpingsheng/p/8635777.html