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


671

我想一次更改几个文件,前提是我可以写入所有文件。我想知道是否可以将多个打开的调用与该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

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

Answers:


1051

从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"

大多数情况下,您拥有一组可变的文件,但是您可能想要一个接一个地打开它们。


5
不幸的是,根据contextlib.nested文档,您不应将其用于文件打开:“使用nested()打开两个文件是一个编程错误,因为如果打开文件时抛出异常,第一个文件将不会立即关闭。第二档。”
weronika 2011年

41
有没有一种方法可以with用来打开文件的可变列表?
monkut

23
@monkut:很好的问题(您实际上可以将其作为一个单独的问题提出)。简短的答案:是的,ExitStack从Python 3.3开始。在任何早期版本的Python中,没有简单的方法可以做到这一点。
Sven Marnach 2013年

12
是否可以使此语法跨越多行?
tommy.carstensen 2014年

9
@ tommy.carstensen:您可以使用常规的行连续机制。您可能应该按照PEP 9的建议,使用反斜杠连续行来在逗号处断开。
Sven Marnach 2014年

99

只需替换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

3
您应指定哪些Python版本支持此语法。
Craig McQueen 2014年

58

对于一次打开多个文件或较长的文件路径,将内容分解成多行可能很有用。@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())

1
有了这个缩进,我得到:“ flake8:连续行缩进了视觉缩进”
Louis M

@LouisM听起来像是来自您的编辑器或环境的东西,而不是来自基本python的东西。如果您仍然遇到问题,建议您创建一个与此相关的新问题,并提供有关您的编辑器和环境的更多详细信息。
Michael Ohlrogge,

3
是的,它绝对是我的编辑器,仅是警告。我想强调的是,您的缩进不符合PEP8。您应该使第二个open()缩进8个空格,而不是与第一个空格对齐。
路易M

2
@LouisM PEP8是一个准则,而不是规则,在这种情况下,我很可能会忽略它
Nick

2
是的,这没有问题,尽管它对其他使用自动短绒的人可能有用:)
Louis M

15

嵌套语句可以完成相同的工作,我认为处理起来更简单。

假设您有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。无重复写入或写入失败。所以我真的很想知道为什么我的答案被认为是错误的,次优的或类似的东西。


1
我不知道其他人对您的否决,但是我支持您,因为这是唯一一个只有三个文件(一个输入,两个输出)的示例,而这个文件恰好正是我所需要的。
亚当·迈克尔·伍德

2
也许您不赞成使用Python> 2.6中的bcoz,您可以编写更多pythonic代码 -gist.github.com/IaroslavR/3d8692e2a11e1ef902d2d8277eb88cb8(为什么我不能在注释中插入代码片段?!)我们在2018年;)如此古老的版本过去
El Ruso

2
温馨提示python 2.6:CentOS 6(直到2020年11月才停止销售),默认情况下仍使用py2.6。因此,此答案(到目前为止)仍然是最好的整体IMO。
BJ黑色

11

因为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

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


1

回答较晚(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
...
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.