如何包装或嵌入发电机?

问题描述 投票:1回答:1

我正在尝试提供一个统一的界面,用于从单个目录或目录列表中检索所有文件。

def get_files(dir_or_dirs):
    def helper(indir):
        file_list = glob.glob("*.txt")
        for file in file_list:
            yield file

    if type(dir_or_dirs) is list:
        # a list of source dirs
        for dir in dir_or_dirs:
            yield helper(dir)
    else:
        # a single source dir
        yield helper(dir_or_dirs)

def print_all_files(file_iter):
    for file in file_iter:
        print(file)        # error here!

问题:

  1. 错误说“文件”仍然是一个生成器,无论输入是单个目录还是列表。为什么它仍然是发电机?
  2. 是否可以在函数中包装或嵌入生成器?如果是这样,如何使这项工作?
python generator yield
1个回答
4
投票

你每次都屈服于helper()

yield helper(dir)

helper()本身就是一个发电机。

在Python 3.3及更高版本中,使用yield from代替:

yield from helper(dir)

这将控制委托给另一台发电机。来自Yield expressions文档:

使用yield from <expr>时,它将提供的表达式视为子参与者。该子转换器生成的所有值都直接传递给当前生成器方法的调用者。

在较旧的Python版本中,包括Python 2.x,使用另一个循环:

for file in helper(dir):
    yield file

有关yield from的更多信息,请参阅PEP 380 -- Syntax for Delegating to a Subgenerator

并不是说你真的需要帮助函数,它只是循环glob.glob()结果,你可以直接做到这一点。

你还需要纠正你的功能才能真正使用indir;目前您忽略了该参数,因此您只能从当前工作目录中获取文本文件。

接下来,你想使用glob.iglob()而不是glob.glob()来获得对os.scandir()的懒惰评估,而不是立即将所有结果加载到内存中。我只是将一个非列表的dir_or_dirs值转换为一个列表,然后只使用一个循环:

import glob
import os.path

def get_files(dirs):
    if not isinstance(dirs, list):
        # make it a list with one element
        dirs = [dirs]

    for dir in dirs:
        pattern = os.path.join(dir, '*.txt')
        yield from glob.iglob(pattern)

现在,我使用*args参数语法,而不是单个参数,而不是字符串或列表,而是使用可变数量的参数:

def get_files(*dirs):
    for dir in dirs:
        pattern = os.path.join(dir, '*.txt')
        yield from glob.iglob(pattern)

可以使用0个或更多目录调用它:

for file in get_files('/path/to/foo', '/path/to/bar'):
    # ...
© www.soinside.com 2019 - 2024. All rights reserved.