Python 同时读取多个文件

问题:假设有多个 行数一样  的文件,如何使用 python 同时对这些文件按行进行读取?

方案一:

解决这个问题需要用到 python 的生成器,实现代码如下:

filenames = ['a.txt', 'b.txt', 'c.txt']

def gen_line(fname):
    with open(fname, 'r', encoding='utf-8') as f:
        for line in f:
            yield line.strip()

gens = [gen_line(fname) for fname in filenames]

for a, b, c in zip(*gens):
    print('\t'.join([a, b, c]))

示例代码中的操作可分为四个步骤:

  1. 将文件名放入一个 list
  2. 定义一个按行读取文件的生成器
  3. 使用使用列表推导式生成一个生成器的列表(这里也可以换用生成器表达式)
  4. 使用 * 语法拆包,然后使用 zip 进行迭代输出

注意:如果文件的行数不一致,zip 会在迭代完最少行数的文件后退出,且不会有任何异常抛出。如需要保证迭代完所有文件内容,可使用 itertools.zip_longest 替代。

方案二:

可能会有人觉得上面的解决方案有一点啰嗦,下面提供一个更加精简的解决方案,此方案来自 stackoverflow 上面的一篇回答:

from contextlib import ExitStack

filenames = ['a.txt', 'b.txt', 'c.txt']

with ExitStack() as stack:
    files = [stack.enter_context(open(fname)) for fname in filenames]
    for rows in zip(*files):
        print('\t'.join([x.strip() for x in rows]))

这个方案使用了python 3.3 引入的 contextlib.ExitStack 用于管理文件关闭操作。代码看起来比方案一精简了不少。不过,如果让我来选择的话,我更倾向于使用方案一。原因如下:

  1. 虽然我非常喜欢精简的代码,行数越少的代码我越喜欢。但是,代码的可读性更为重要。方案二使用了一个并不那么普及的方法,或者说所谓的“高级特性”,代码确实简洁了不少,但却大大降低了代码的可读性,不可取。代码是写给人看的,机器只负责执行它。推荐一篇文章《为什么高级程序员写的代码都是傻瓜式的?》。一句话,可读性优于代码精简
  2. 方案二虽然精简,但真正用起来可能并没有方案一方便,看一下 strip 方法的调用位置就能明白。方案一之所以显得啰嗦,是因为专门为打开文件定义了一个生成器。但是,这个生成器可以在其它任何需要打开文件的地方复用,而且用起来非常方便。也许将来某一天,类似的生成器会直接进入 python 的标准库也说不定。
  3. 即便是完全使用 python 3,版本的兼容性在很多情况下依然是要需要考虑的。
  4. 方案一是我自己想出来的,当然会觉得好。(皮一下,很开心!)

最后,以上仅为个人观点,方案二其实也挺好的。