Làm cách nào tôi có thể mở nhiều tệp bằng cách sử dụng với với mở mở trong Python?


670

Tôi muốn thay đổi một vài tệp cùng một lúc, nếu tôi có thể viết cho tất cả chúng. Tôi tự hỏi nếu bằng cách nào đó tôi có thể kết hợp nhiều cuộc gọi mở với withcâu lệnh:

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

Nếu điều đó là không thể, một giải pháp tao nhã cho vấn đề này sẽ như thế nào?

Câu trả lời:


1050

Kể từ Python 2.7 (hoặc 3.1 tương ứng), bạn có thể viết

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

Trong các phiên bản trước của Python, đôi khi bạn có thể sử dụng contextlib.nested()để lồng các trình quản lý bối cảnh. Tuy nhiên, điều này sẽ không hoạt động như mong đợi để mở nhiều tệp, - xem tài liệu được liên kết để biết chi tiết.


Trong trường hợp hiếm hoi mà bạn muốn mở một số lượng tệp khác nhau cùng một lúc, bạn có thể sử dụng contextlib.ExitStack, bắt đầu từ Python phiên bản 3.3:

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

Hầu hết thời gian bạn có một tập hợp các tệp khác nhau, bạn có thể muốn mở từng cái một.


5
Thật không may, theo các tài liệu bối cảnh, bạn không nên sử dụng nó để mở tệp: "sử dụng lồng nhau () để mở hai tệp là lỗi lập trình vì tệp đầu tiên sẽ không được đóng ngay lập tức nếu ném ngoại lệ khi mở tập tin thứ hai. "
weronika

41
Có cách nào để sử dụng withđể mở danh sách các tệp khác nhau không?
monkut

23
@monkut: Câu hỏi rất hay (bạn thực sự có thể hỏi đây là một câu hỏi riêng biệt). Câu trả lời ngắn: Có, có ExitStacktừ Python 3.3. Không có cách nào dễ dàng để làm điều này trong bất kỳ phiên bản Python nào trước đó.
Sven Marnach

12
Có thể có cú pháp này kéo dài nhiều dòng?
tommy.carstensen

9
@ tommy.carstensen: Bạn có thể sử dụng các cơ chế tiếp tục dòng thông thường . Bạn có thể nên sử dụng tiếp tục dấu gạch chéo ngược để ngắt ở dấu phẩy, như được đề xuất bởi PEP 9 .
Sven Marnach

99

Chỉ cần thay thế andvới ,và bạn thực hiện xong:

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
Bạn nên chỉ định phiên bản nào của Python hỗ trợ cú pháp này.
Craig McQueen

58

Để mở nhiều tệp cùng một lúc hoặc cho các đường dẫn tệp dài, có thể hữu ích để chia nhỏ mọi thứ qua nhiều dòng. Từ Hướng dẫn về Phong cách Python như được đề xuất bởi @Sven Marnach trong các nhận xét cho câu trả lời khác:

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
Với sự thụt lề này, tôi nhận được: "flake8: dòng tiếp tục thụt vào quá mức cho thụt vào thị giác"
Louis M

@LouisM Nghe có vẻ như một cái gì đó đến từ trình soạn thảo hoặc môi trường của bạn, chứ không phải là python cơ sở. Nếu nó tiếp tục là một vấn đề đối với bạn, tôi khuyên bạn nên tạo một câu hỏi mới liên quan đến nó và cung cấp thêm chi tiết về trình soạn thảo và môi trường của bạn.
Michael Ohlrogge

3
Vâng, nó chắc chắn là biên tập viên của tôi, và nó chỉ là một cảnh báo. Điều tôi muốn nhấn mạnh là sự thụt lề của bạn không tuân thủ PEP8. Bạn nên thụt lề mở thứ hai () với 8 khoảng trắng thay vì căn chỉnh nó với khoảng trống thứ nhất.
Louis M

2
@LouisM PEP8 là một hướng dẫn , không phải là quy tắc và trong trường hợp này tôi chắc chắn sẽ bỏ qua nó
Nick

2
Có, không có vấn đề gì với điều đó, nó có thể hữu ích cho những người khác với máy tự động mặc dù :)
Louis M

14

Lồng ghép với các tuyên bố sẽ làm cùng một công việc, và theo tôi, là đơn giản hơn để giải quyết.

Giả sử bạn có inFile.txt và muốn viết nó thành hai 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)

BIÊN TẬP:

Tôi không hiểu lý do của downvote. Tôi đã kiểm tra mã của mình trước khi xuất bản câu trả lời của mình và nó hoạt động như mong muốn: Nó viết cho tất cả các outFile, giống như câu hỏi yêu cầu. Không viết trùng lặp hoặc không viết. Vì vậy, tôi thực sự tò mò muốn biết tại sao câu trả lời của tôi bị coi là sai, không tối ưu hoặc bất cứ điều gì như vậy.


1
Tôi không biết người khác đánh giá thấp bạn là gì, nhưng tôi đã TĂNG LÊN bạn bởi vì đây là ví dụ duy nhất có ba tệp (một đầu vào, hai đầu ra) là thứ tôi cần.
Adam Michael Wood

2
có thể bạn bị hạ cấp bcoz trong Python> 2.6 bạn có thể viết thêm mã pythonic - gist.github.com/IaroslavR/3d8692e2a11e1ef902d2d8277eb88cb8 (tại sao tôi không thể chèn đoạn mã trong các nhận xét?!) quá khứ
El Ruso

2
Một lời nhắc nhở thân thiện với những con trăn poo-poo Breath 2.6: CentOS 6 (không phải EOL cho đến tháng 11 năm 2020), vẫn sử dụng py2.6 theo mặc định. Vì vậy, câu trả lời này là (tính đến thời điểm hiện tại) vẫn là một IMO tổng thể tốt nhất.
BJ Đen

11

Kể từ Python 3.3, bạn có thể sử dụng lớp ExitStacktừ contextlibmô-đun để
mở một cách an toàn số lượng tệp tùy ý .

Nó có thể quản lý một động số đối tượng nhận biết ngữ cảnh, có nghĩa là nó sẽ chứng minh đặc biệt hữu ích nếu bạn không biết có bao nhiêu dữ liệu mà bạn đang đi để xử lý .

Trong thực tế, trường hợp sử dụng chính tắc được đề cập trong tài liệu này đang quản lý một số lượng tệp động.

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

Nếu bạn quan tâm đến các chi tiết, đây là một ví dụ chung để giải thích cách ExitStackvận hành:

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)

Đầu ra:

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

3

Với python 2.6 Nó sẽ không hoạt động, chúng tôi phải sử dụng cách bên dưới để mở nhiều tệp:

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

1

Câu trả lời muộn (8 năm), nhưng đối với ai đó muốn tham gia nhiều tệp vào một , chức năng sau đây có thể giúp ích:

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
...
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.