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

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

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

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

I want to change a couple of files at one time, iff I can write to all of them. I’m wondering if I somehow can combine the multiple open calls with the with statement:

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

If that’s not possible, what would an elegant solution to this problem look like?


回答 0

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

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

As of Python 2.7 (or 3.1 respectively) you can write

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

In earlier versions of Python, you can sometimes use contextlib.nested() to nest context managers. This won’t work as expected for opening multiples files, though — see the linked documentation for details.


In the rare case that you want to open a variable number of files all at the same time, you can use contextlib.ExitStack, starting from Python version 3.3:

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

Most of the time you have a variable set of files, you likely want to open them one after the other, though.


回答 1

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

Just replace and with , and you’re done:

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

回答 2

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

For opening many files at once or for long file paths, it may be useful to break things up over multiple lines. From the Python Style Guide as suggested by @Sven Marnach in comments to another answer:

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

回答 3

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

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

Nested with statements will do the same job, and in my opinion, are more straightforward to deal with.

Let’s say you have inFile.txt, and want to write it into two outFile’s simultaneously.

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)

EDIT:

I don’t understand the reason of the downvote. I tested my code before publishing my answer, and it works as desired: It writes to all of outFile’s, just as the question asks. No duplicate writing or failing to write. So I am really curious to know why my answer is considered to be wrong, suboptimal or anything like that.


回答 4

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

Since Python 3.3, you can use the class ExitStack from the contextlib module to safely
open an arbitrary number of files.

It can manage a dynamic number of context-aware objects, which means that it will prove especially useful if you don’t know how many files you are going to handle.

In fact, the canonical use-case that is mentioned in the documentation is managing a dynamic number of files.

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

If you are interested in the details, here is a generic example in order to explain how ExitStack operates:

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)

Output:

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

回答 5

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

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

With python 2.6 It will not work, we have to use below way to open multiple files:

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

回答 6

回答较晚(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
...

Late answer (8 yrs), but for someone looking to join multiple files into one, the following function may be of help:

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
...