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

940

如果我可以写入所有文件,我想同时更改几个文件。 我想知道是否可以使用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

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


4
同样的问题:在with语句中使用多个变量? - Jeyekomon
8个回答

1434

从Python 2.7(或3.1)开始,您可以编写:

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

历史注释:在早期版本的Python中,有时可以使用contextlib.nested()来嵌套上下文管理器。然而,这对于打开多个文件并非如预期般有效--请查看相关文档以了解详情。)


在极少数情况下,如果您想要同时打开数量可变的文件,您可以从Python 3.3开始使用contextlib.ExitStack

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

请注意,通常情况下,您希望按顺序处理文件,而不是同时打开所有文件,特别是如果您有数量可变的文件:

for fname in filenames:
    with open(fname) as f:
        # Process f

9
不幸的是,根据contextlib.nested文档的描述,你不应该使用它来打开文件:"使用nested()打开两个文件是一个编程错误,因为如果在打开第二个文件时发生异常,第一个文件将不会被及时关闭。" - weronika
57
可以使用 with 打开一个变量列表的文件吗? - monkut
31
@monkut: 这是一个非常好的问题(实际上你可以将它作为一个单独的问题提出)。简短回答:是的,在Python 3.3及以上版本中有ExitStack。(http://docs.python.org/3/library/contextlib.html#contextlib.ExitStack)在任何早期版本的Python中都没有简单的方法来实现这一点。 - Sven Marnach
16
这个语法可以跨多行吗? - tommy.carstensen
11
@tommy.carstensen:您可以使用常规的行连续机制。 您应该使用反斜杠行连续来在逗号处断开,这是PEP 9推荐的方式 - Sven Marnach
显示剩余7条评论

131

只需要将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

13
请说明哪些版本的Python支持这种语法。 - Craig McQueen

98

为了一次打开多个文件或者处理很长的文件路径,将代码分成多行可能会很有用。来自Python风格指南,建议按照@Sven Marnach在另一个答案的评论中提到的方式操作:

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())

2
由于这个缩进,我得到了以下错误提示: “flake8:可视缩进的连续行超出了缩进范围” - Louis M
1
@LouisM 这听起来像是来自于你的编辑器或环境,而不是基本的Python。如果这对你来说仍然是一个问题,我建议你创建一个新的问题,并在其中提供更多关于你的编辑器和环境的细节。 - Michael Ohlrogge
5
是的,这绝对是我的编辑器发出的警告。我想强调的是,你的缩进不符合PEP8标准。你应该将第二个open()缩进8个空格,而不是与第一个对齐。 - Louis M
4
PEP8是一份“指南”,而不是规则,在这种情况下,我肯定会忽略它。 - Nick stands with Ukraine
4
没问题,虽然这可能对使用自动化代码检查工具的其他人有所帮助 :) - Louis M
非常好!我一直在苦苦挣扎,试图找到一种更易读的使用方式,而这个方法做到了! - Fontanka16

56

从Python 3.10开始,出现了一个名为“括号内的上下文管理器”的新特性,它允许类似以下语法的操作:

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

3
有趣。增加了一对额外的括号。它能做到 with open("a", "w") as a, open ("b", "w") as b: 无法做到的吗? - PatrickT
11
它允许将语法分成多行而不换行,这对于较长的示例可能更易读。 - Chris_Rands
它在这里的问题12782中进行了描述:https://bugs.python.org/issue12782 - tommy.carstensen

34
自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]

29

使用嵌套的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中。没有重复写入或未能写入。因此,我真的很想知道为什么我的答案被认为是错误的、次优的或其他类似的东西。


2
我不知道别人为什么给你点了踩,但我给你点了赞,因为这是唯一一个有三个文件(一个输入,两个输出)的例子,而这正好是我所需要的。 - Adam Michael Wood
3
@FatihAkici Python之禅中说:“扁平比嵌套更好”。不必要的嵌套代码会降低可读性,被认为是一种不良实践。 - Jeyekomon
@ElRuso 为什么更符合 Python 风格?缩进更少吗? - gargoylebident
@stackexchange_account1111 是的,在你的问题上面有更详细的答案。 - El Ruso
三个文件已经达到(或超过)嵌套或括号逗号分隔的上限,此时切换到ExitStack是合适的。我喜欢这种明确的方式,但如果函数在其输入中执行更多操作,则会将第四个级别包装在单独的函数fanout(fr,fw1,fw2)中以重置缩进,特别是在示例中未显示的情况下。 - ojdo

6

在Python 2.6版本中,这种方法不可行。我们需要使用以下方式来打开多个文件:

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

4
拖延的回答(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
...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接