Python递归中使用协程yield

作者:matrix 发布时间:2019年6月11日星期二 分类:Python

修改递归函数用于遍历目录中文件。

普通操作

def recursive_open_file(path):
    rel = []
    path_dir = os.listdir(path)  # 获取当前路径下的文件名,返回List
    for s in path_dir:
        new_dir = os.path.join(path, s)  # 将文件命加入到当前文件路径后面
        if os.path.isfile(new_dir):  # 如果是文件
            if os.path.splitext(new_dir)[1] == ".txt":  # 判断是否是txt
                rel.append(new_dir)
        else:
            rel = rel + recursive_open_file(new_dir)
    return rel

# print(type(recursive_open_file(dir))) # <class 'list'>

调试yield

想用协程目的是为了想让程序找到相关文件之后中断挂起然后返回数据,避免一次性加载全部资源在内存中。

之前是想的太简单,没用过果真是不知道。

错误版本

def recursive_open_file(path):
    path_dir = os.listdir(path)  # 获取当前路径下的文件名,返回List
    for s in path_dir:
        new_dir = os.path.join(path, s)  # 将文件命加入到当前文件路径后面
        if os.path.isfile(new_dir):  # 如果是文件
            if os.path.splitext(new_dir)[1] == ".txt":  # 判断是否是txt
                yield new_dir
        else:
            yield recursive_open_file(new_dir)

#执行
for i in recursive_open_file(dir):
    print(i) #无法获取文件路径

说明:
yield recursive_open_file(new_dir)返回给外部调用层的数据为<generator object recursive_open_file at 0x10f7765e8> ,不是想要的String!!

正常版本

内部再迭代下就好了 🙈

def recursive_open_file(path):
    path_dir = os.listdir(path)  # 获取当前路径下的文件名,返回List
    for s in path_dir:
        new_dir = os.path.join(path, s)  # 将文件命加入到当前文件路径后面
        if os.path.isfile(new_dir):  # 如果是文件
            if os.path.splitext(new_dir)[1] == ".txt":  # 判断是否是txt
                yield new_dir
        else:
            for i in recursive_open_file(new_dir):
                yield i

PHP协程递归同理


function openDirectory($path) { $dir = dir($path); while (false != ($entry = $dir->read())) { if ($entry != "." && $entry != "..") { $n_path = $path . DIRECTORY_SEPARATOR . $entry; if (is_dir($n_path)) { foreach (openDirectory($n_path) as $i){ yield $i; } } else { yield $n_path; } } } } //调用执行 $dir = '/Users/panc/Desktop/Python/testfile'; foreach (openDirectory($dir) as $item){ print_r($item); print_r("\n"); }

协程send操作

按照廖雪峰的协程教程的生产者-消费者模式:

def consumer():
    r = ''
    while True:
        n = yield r
        if not n:
            return
        print('[CONSUMER] Consuming %s...' % n)
        r = '200 OK'

def produce(c):
    c.send(None)#启动
    n = 0
    while n < 5:
        n = n + 1
        print('[PRODUCER] Producing %s...' % n)
        r = c.send(n)
        print('[PRODUCER] Consumer return: %s' % r)
    c.close()#关闭

c = consumer()
produce(c)

笔记:
c = consumer()不会执行consumer函数,因为内部有yield关键字,consumer函数是生成器generator对象。
通过多个断点调适可以看到yield处的代码会中断执行,然后切换到起调函数的位置继续执行
yield r相似于return返回数据,返回给send()方法返回值。
send(n)操作是把n发送给yield r的返回值
c.send(None)用于启动consumer函数,程序会进入while True循环,在yield处中断

这里yield操作相比较递归遍历那头来说更加麻烦些,因为执行的时候会在两个函数之间相互切换,互相发送数据,需要send方法来启动生成器generator对象。consumer内部因为是while true,所以记得要关闭c.close()

参考:
https://www.liaoxuefeng.com/wiki/1016959663602400/1017968846697824
https://github.com/Earthson/RecGen
https://blog.csdn.net/mieleizhi0522/article/details/82142856

https://blog.51cto.com/xtceetg/1874982

PEACE~