如何在Python中使用"with open"打开多个文件?

Fra*_*003 615 python file-io

我想一次更改几个文件,如果我可以写入所有文件.我想知道我是否能以某种方式将多个开放调用与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
Run Code Online (Sandbox Code Playgroud)

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

Sve*_*ach 956

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

with open('a', 'w') as a, open('b', 'w') as b:
    do_something()
Run Code Online (Sandbox Code Playgroud)

在早期版本的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"
Run Code Online (Sandbox Code Playgroud)

大多数情况下,如果你有一组可变文件,你可能想要一个接一个地打开它们.

  • 有没有办法使用`with`来打开一个变量文件列表? (36认同)
  • @monkut:非常好的问题(你实际上可以将此问题作为一个单独的问题).简答:是的,从Python 3.3开始有[`ExitStack`](http://docs.python.org/3/library/contextlib.html#contextlib.ExitStack).在任何早期版本的Python中都没有简单的方法可以做到这一点. (22认同)
  • 是否可以将此语法跨越多行? (12认同)
  • @ tommy.carstensen:您可以使用通常的[行继续机制](https://docs.python.org/2.7/reference/lexical_analysis.html#explicit-line-joining).您应该使用反斜杠行继续以逗号分隔,如[PEP 9推荐](http://legacy.python.org/dev/peps/pep-0008/#maximum-line-length). (9认同)
  • 不幸的是,根据contextlib.nested文档,您不应该将它用于文件打开:"使用嵌套()打开两个文件是一个编程错误,因为如果在打开时抛出异常,第一个文件将不会立即关闭第二档." (5认同)
  • @monkut你绝对应该问你的问题,因为我有完全相同的问题。contextlib.ExitStack 似乎就是答案。 (2认同)
  • @ thanos.a你可以使用`for line_a,line_b in zip(a,b)`,其中`a`和`b`是上面的两个文件对象.(一般情况下,请先询问一个新问题,而不是评论,甚至更好,先搜索现有问题.) (2认同)

Mic*_*ael 94

只需更换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
Run Code Online (Sandbox Code Playgroud)

  • 您应指定哪些Python版本支持此语法。 (2认同)

Mic*_*gge 50

要一次打开多个文件或长文件路径,在多行中分解可能很有用.从@Sven Marnach建议的Python风格指南到另一个答案的评论:

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())
Run Code Online (Sandbox Code Playgroud)

  • 是的,没问题,但它可能对其他使用自动短绒的人有用:) (3认同)
  • 通过这个缩进,我得到:“flake8:连续线过度缩进以实现视觉缩进” (2认同)
  • 是的,它绝对是我的编辑器,仅是警告。我想强调的是,您的缩进不符合PEP8。您应该使第二个open()缩进8个空格,而不是与第一个空格对齐。 (2认同)
  • @LouisM PEP8 是*指南*,而不是规则,在这种情况下,我肯定会忽略它 (2认同)

Fat*_*ici 11

嵌套语句将完成相同的工作,在我看来,更直接的处理.

假设您有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)
Run Code Online (Sandbox Code Playgroud)

编辑:

我不明白downvote的原因.我在发布我的答案之前测试了我的代码,并且它按照需要工作:它写入所有的outFile,就像问题一样.没有重复写入或无法写入.所以我很想知道为什么我的答案被认为是错误的,次优的或类似的.

  • 也许是因为最初的问题是要求组合而不是嵌套.也就是说,嵌套似乎太明显了,不成问题.只是一个随意的想法.无论如何,这是最普遍的答案. (8认同)
  • @FatihAkici Python 之禅说:“扁平比嵌套更好”。不必要的嵌套代码会降低可读性,被认为是一种不好的做法。 (3认同)
  • 我不知道其他人对你投了反对票,但我对你投了赞成票,因为这是唯一一个拥有三个文件(一个输入,两个输出)的示例,而这恰好正是我所需要的。 (2认同)
  • 也许你在Python> 2.6中投票bcoz你可以写更多的pythonic代码 - https://gist.github.com/IaroslavR/3d8692e2a11e1ef902d2d8277eb88cb8(为什么我不能在评论中插入代码片段?!)我们在2018年;)过去如此古老的版本 (2认同)
  • 友好提醒那些对 python 2.6 嗤之以鼻的人:CentOS 6(要到 2020 年 11 月才会停产)默认情况下仍然使用 py2.6。所以这个答案(截至目前)在我看来仍然是最好的答案。 (2认同)

Chr*_*nds 11

从 Python 3.10 开始,有括号上下文管理器的新功能,它允许使用以下语法:

with (
    open("a", "w") as a,
    open("b", "w") as b
):
    do_something()
Run Code Online (Sandbox Code Playgroud)

  • @PatrickT它允许将语法拆分为多行而不换行,这对于长示例来说可能更具可读性 (11认同)
  • 有趣的。添加一对额外的括号。它能做任何 `with open("a", "w") as a, open ("b", "w") as b:` 做不到的事情吗? (3认同)

tim*_*geb 8

因为Python 3.3,你可以使用类ExitStackcontextlib模块到安全地
打开文件的任意数量

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

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

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
Run Code Online (Sandbox Code Playgroud)

如果您对这些细节感兴趣,下面是一个通用示例,以说明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)
Run Code Online (Sandbox Code Playgroud)

输出:

0
enter X1
enter X2
enter X3
3
exit X3
exit X2
exit X1
0
[1, 2, 3]
Run Code Online (Sandbox Code Playgroud)


小智 6

使用 python 2.6 它将无法工作,我们必须使用以下方式来打开多个文件:

with open('a', 'w') as a:
    with open('b', 'w') as b:
Run Code Online (Sandbox Code Playgroud)