【问题标题】:How can I open multiple files using "with open" in Python?如何在 Python 中使用“with open”打开多个文件?
【发布时间】:2011-06-04 18:14:41
【问题描述】:

我想一次更改几个文件,如果我可以全部写入。我想知道我是否可以以某种方式将多个打开调用与with 语句结合起来:

try:
  with open('a', 'w') as a and open('b', 'w') as b:
    do_something()
except IOError as e:
  print 'Operation failed: %s' % e.strerror

如果这不可能,那么该问题的优雅解决方案应该是什么样的?

【问题讨论】:

标签: python file-io


【解决方案1】:

从 Python 2.7(或分别为 3.1)开始,您可以编写

with open('a', 'w') as a, open('b', 'w') as b:
    do_something()

在早期版本的 Python 中,您有时可以使用 contextlib.nested() 嵌套上下文管理器。但是,这在打开多个文件时不会按预期工作——有关详细信息,请参阅链接文档。


在极少数情况下,您想同时打开不定数量的文件,您可以使用contextlib.ExitStack,从 Python 版本 3.3 开始:

with ExitStack() as stack:
    files = [stack.enter_context(open(fname)) for fname in filenames]
    # Do something with "files"

大多数时候,您有一组可变的文件,但您可能希望一个接一个地打开它们。

【讨论】:

  • 不幸的是,根据 contextlib.nested 文档,您不应该使用它来打开文件:“使用 nested() 打开两个文件是一个编程错误,因为第一个文件不会立即关闭如果打开第二个文件时抛出异常。"
  • 有没有办法使用with打开文件的变量列表?
  • @monkut:非常好的问题(您实际上可以将其作为一个单独的问题提出)。简短的回答:是的,从 Python 3.3 开始就有 ExitStack。在任何早期版本的 Python 中都没有简单的方法可以做到这一点。
  • 这种语法可以跨多行吗?
  • @tommy.carstensen:您可以使用通常的line continuation mechanisms。您可能应该使用反斜杠继续在逗号处中断,如recommended by PEP 9
【解决方案2】:

只需将and 替换为, 即可完成:

try:
    with open('a', 'w') as a, open('b', 'w') as b:
        do_something()
except IOError as e:
    print 'Operation failed: %s' % e.strerror

【讨论】:

  • 你应该指定哪些版本的 Python 支持这种语法。
【解决方案3】:

对于一次打开多个文件或文件路径较长的情况,将内容拆分为多行可能很有用。从@Sven Marnach 在 cmets 中建议的Python Style Guide 到另一个答案:

with open('/path/to/InFile.ext', 'r') as file_1, \
     open('/path/to/OutFile.ext', 'w') as file_2:
    file_2.write(file_1.read())

【讨论】:

  • 有了这个缩进,我得到:“flake8: continuation line over-indented for visual indent”
  • @LouisM 这听起来像是来自您的编辑器或环境,而不是基础 python。如果它仍然是您的问题,我建议创建一个与之相关的新问题,并提供有关您的编辑器和环境的更多详细信息。
  • 是的,它绝对是我的编辑器,它只是一个警告。我想强调的是,您的缩进不符合 PEP8。您应该将第二个 open() 缩进 8 个空格,而不是与第一个对齐。
  • @LouisM PEP8 是指南,而不是规则,在这种情况下我肯定会忽略它
  • 是的,这没问题,不过它可能对其他使用自动 linter 的人有用:)
【解决方案4】:

从 Python 3.3 开始,您可以使用 contextlib 模块中的类 ExitStack 安全地
打开任意数量的文件

它可以管理一个动态数量的上下文感知对象,这意味着它会证明特别有用如果你不知道你要处理多少个文件.

事实上,文档中提到的规范用例是管理动态数量的文件。

with ExitStack() as stack:
    files = [stack.enter_context(open(fname)) for fname in filenames]
    # All opened files will automatically be closed at the end of
    # the with statement, even if attempts to open files later
    # in the list raise an exception

如果你对细节感兴趣,这里有一个通用的例子来解释ExitStack是如何运作的:

from contextlib import ExitStack

class X:
    num = 1

    def __init__(self):
        self.num = X.num
        X.num += 1

    def __repr__(self):
        cls = type(self)
        return '{cls.__name__}{self.num}'.format(cls=cls, self=self)

    def __enter__(self):
        print('enter {!r}'.format(self))
        return self.num

    def __exit__(self, exc_type, exc_value, traceback):
        print('exit {!r}'.format(self))
        return True

xs = [X() for _ in range(3)]

with ExitStack() as stack:
    print(len(stack._exit_callbacks)) # number of callbacks called on exit
    nums = [stack.enter_context(x) for x in xs]
    print(len(stack._exit_callbacks))

print(len(stack._exit_callbacks))
print(nums)

输出:

0
enter X1
enter X2
enter X3
3
exit X3
exit X2
exit X1
0
[1, 2, 3]

【讨论】:

    【解决方案5】:

    嵌套 with 语句会做同样的工作,而且在我看来,处理起来更简单。

    假设你有 inFile.txt,想同时写入两个 outFile。

    with open("inFile.txt", 'r') as fr:
        with open("outFile1.txt", 'w') as fw1:
            with open("outFile2.txt", 'w') as fw2:
                for line in fr.readlines():
                    fw1.writelines(line)
                    fw2.writelines(line)
    

    编辑:

    我不明白投反对票的原因。我在发布答案之前测试了我的代码,它按预期工作:它写入所有 outFile,正如问题所要求的那样。没有重复写作或失败写作。所以我真的很想知道为什么我的答案被认为是错误的、次优的或类似的。

    【讨论】:

    • 我不知道其他人对你投了反对票,但我对你投了赞成票,因为这是唯一一个包含三个文件(一个输入,两个输出)的示例,而这恰好正是我所需要的。
    • 也许你在 Python 中被否决了 bcoz > 2.6 你可以写更多的 Pythonic 代码 - gist.github.com/IaroslavR/3d8692e2a11e1ef902d2d8277eb88cb8(为什么我不能在 cmets 中插入代码片段?!)我们在 2018 年;)如此古老过去的版本
    • 对那些讨厌 python 2.6 的人友好提醒:CentOS 6(直到 2020 年 11 月才会 EOL),默认情况下仍然使用 py2.6。所以这个答案(截至目前)仍然是 IMO 总体上最好的答案。
    • @FatihAkici Python 的禅宗说:“扁平比嵌套更好”。不必要的嵌套代码会降低可读性,被认为是一种不好的做法。
    • @ElRuso 为什么它更 Pythonic?缩进更少?
    【解决方案6】:

    从 Python 3.10 开始,Parenthesized context managers 有了一个新特性,它允许如下语法:

    with (
        open("a", "w") as a,
        open("b", "w") as b
    ):
        do_something()
    

    【讨论】:

    • 有趣。添加一对额外的括号。它能做with open("a", "w") as a, open ("b", "w") as b:做不到的事情吗?
    • @PatrickT 它允许将语法拆分为多行而没有换行符,这对于长示例可能更具可读性
    • 知道了!谢谢克里斯!
    【解决方案7】:

    用python 2.6 不行,我们必须用下面的方式打开多个文件:

    with open('a', 'w') as a:
        with open('b', 'w') as b:
    

    【讨论】:

      【解决方案8】:

      迟到的答案(8 岁),但对于希望将多个文件合并为一个的人,以下功能可能会有所帮助:

      def multi_open(_list):
          out=""
          for x in _list:
              try:
                  with open(x) as f:
                      out+=f.read()
              except:
                  pass
                  # print(f"Cannot open file {x}")
          return(out)
      
      fl = ["C:/bdlog.txt", "C:/Jts/tws.vmoptions", "C:/not.exist"]
      print(multi_open(fl))
      

      2018-10-23 19:18:11.361 PROFILE  [Stop Drivers] [1ms]
      2018-10-23 19:18:11.361 PROFILE  [Parental uninit] [0ms]
      ...
      # This file contains VM parameters for Trader Workstation.
      # Each parameter should be defined in a separate line and the
      ...
      

      【讨论】:

        猜你喜欢
        • 2014-01-15
        • 2012-03-06
        • 2018-10-29
        • 2014-03-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-06-15
        相关资源
        最近更新 更多