先来看一个例子
- 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
来分析一下上面的代码的执行过程 :
程序开始执行以后, 因为 foo 函数中有 yield 关键字, 所以 foo 函数并不会真的执行, 而是先得到一个生成器 g.
直到调用 next 方法, foo 函数正式开始执行, 先执行 foo 函数中的 print 方法, 然后进入 while 循环
程序遇到 yield 关键字, 程序暂停, 此时 next(g) 语句执行完成
程序执行 g.send(5), 程序会从 yield 关键字那一行继续向下运行, send 会把 5 这个值传递给 yield
yield 接收到 send 方法传递过来的值, 然后由 yield 赋值给 res 变量
由于 send 方法中包含 next() 方法, 所以程序会继续向下运行执行 print 方法, 然后再次进入 while 循环
程序执行再次遇到 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)
来源: https://www.cnblogs.com/renpingsheng/p/8635777.html