【问题标题】:Create Generator From Lines of Multiple Files从多行文件创建生成器
【发布时间】:2018-08-23 15:27:43
【问题描述】:

我有一个主文件和一组附属文件,但我不知道附属文件的名称,直到我查看主文件。

主文件包含两列:一些数据和第二个文件名,例如,

data1_from_master   hidden_file1
data2_from_master   hidden_file2
data3_from_master   hidden_file1
data4_from_master   hidden_file3
data5_from_master   hidden_file1

我想要做的是创建一个生成器,它从主文件的第一列生成一个元素,然后从一个子文件中生成一行数据。例如,

data1_from_master    line1_from_file1
data2_from_master    line1_from_file2
data3_from_master    line2_from_file1
data4_from_master    line1_from_file3
data5_from_master    line3_from_file1

主文件的行数等于所有子文件的行数之和,所以一旦主文件被遍历完,所有的子文件也会被遍历完。

如果我只有两个要打开的文件,并且我事先知道它们的名称,我可以执行类似的操作。

with open(master_file, 'r') as a, open(hidden_file, 'r') as b:
    for line1, line2 in zip(a, b):
        yield (line1, line2)

但难题是,在我读取主文件的给定行之前,我不知道要读取哪个子文件。然后尝试构建一个包含多个不同文件的行的生成器会增加复杂性。

【问题讨论】:

    标签: python file nested generator yield


    【解决方案1】:

    您想使用ExitStack。这是contextlib 库提供的帮助器类,允许组合上下文管理器。它可用于在单个 with 语句中保持打开多个文件。

    from contextlib import ExitStack
    
    def iter_master_file(filename):
        with ExitStack() as stack:
            master = stack.enter_context(open(filename))
            hidden_files = {}
    
            for line in master:
                # You can parse the lines as you like
                # Here I just assume the last word is a file name
                *data, file = line.split()
    
                if file not in hidden_files:
                    hidden_files[file] = stack.enter_context(open(file))
    
                yield ' '.join(data), next(hidden_files[file]).strip()
    

    示例

    让我们为此示例设置一些文件。

    文件

    master.txt

    master says hidden1.txt is: hidden1.txt
    master says hidden2.txt is: hidden2.txt
    master says hidden1.txt is: hidden1.txt
    master says hidden2.txt is: hidden2.txt
    

    hidden1.txt

    I am hidden file 1 line 1
    I am hidden file 1 line 2
    

    hidden2.txt

    I am hidden file 2 line 1
    I am hidden file 2 line 2
    

    这是一个实际的例子。

    代码

    for data, hidden_data in iter_master_file('master.txt'):
        print(data, hidden_data)
    

    输出

    master says hidden1.txt is: I am hidden file 1 line 1
    master says hidden2.txt is: I am hidden file 2 line 1
    master says hidden1.txt is: I am hidden file 1 line 2
    master says hidden2.txt is: I am hidden file 2 line 2
    

    【讨论】:

    • 很棒的答案。谢谢。
    【解决方案2】:

    您可以保留打开文件的“缓存”并在需要时调用fileobj.readline()

    def read_master_file(master):
        other_files = {}
        for line in master:
            data, name = line.split()
            if name not in otherfiles:
                other_files[name] = open(name)
            yield data, other_files[name].readline()
        for f in other_files.values():
            f.close()
    

    用作:

    with open('master') as master:
        for data, line in read_master_file(master):
            # do stuff
    

    不幸的是,这是您必须使用没有with 的文件的情况之一,因为您不知道要处理多少个文件。

    可以编写一个自定义上下文管理器来保存“缓存”,以实现如下目的:

    def read_master_file(master):
        with OtherFiles() as other_files:
            for line in master:
                data, name = line.split()
                yield data, other_files.get_file(name).readline()
    

    get_file 将在哪里查找缓存并可能打开文件,OtherFiles()__exit__ 方法将关闭打开的文件。

    但如果这是唯一使用它的地方,那它就没有意义了。

    【讨论】:

      猜你喜欢
      • 2016-03-17
      • 1970-01-01
      • 1970-01-01
      • 2018-03-20
      • 1970-01-01
      • 2023-03-09
      • 2012-12-18
      • 2019-05-11
      相关资源
      最近更新 更多