2013-10-16 17 views
14

Lưu ý: tôi biết cú phápMở danh sách các tập tin sử dụng với/như quản lý bối cảnh

with open('f1') as f1, open('f2') as f2: 
    ... 

. Đây là một câu hỏi khác.


Cho một danh sách các chuỗi file_names là có một cách sử dụng with/as để mở tất cả các tên file trong đó sử dụng một dòng duy nhất. Một cái gì đó như:

with [open(fn) for fn in file_names] as files: 
    # use the list of files 

tất nhiên không hoạt động khi cố gắng sử dụng trình quản lý ngữ cảnh trong danh sách. Độ dài của danh sách có thể không được biết cho đến khi thời gian chạy, chẳng hạn như sys.argv[1:]

+0

bạn có thể viết trình quản lý ngữ cảnh của riêng mình. đó là một lựa chọn? nó khá dễ dàng. http://docs.python.org/release/2.5.1/ref/context-managers.html –

Trả lời

9

Nếu bạn có quyền truy cập vào Python 3.3+, có một lớp học đặc biệt được thiết kế chính xác cho mục đích này: ExitStack. Nó hoạt động giống như bạn mong đợi:

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

Làm thế nào về điều này?

class ListContext: 
    def __init__(self, l): 
     self.l = l 

    def __enter__(self): 
     for x in self.l: 
      x.__enter__() 
     return self.l 

    def __exit__(self, type, value, traceback): 
     for x in self.l: 
      x.__exit__(type, value, traceback) 

arr = ['a', 'b', 'c'] 

with ListContext([open(fn, 'w') for fn in arr]) as files: 
    print files 

print files 

Output là:

[<open file 'a', mode 'w' at 0x7f43d655e390>, <open file 'b', mode 'w' at 0x7f43d655e420>, <open file 'c', mode 'w' at 0x7f43d655e4b0>] 
[<closed file 'a', mode 'w' at 0x7f43d655e390>, <closed file 'b', mode 'w' at 0x7f43d655e420>, <closed file 'c', mode 'w' at 0x7f43d655e4b0>] 

Thông báo, họ đang mở bên trong với bối cảnh và đóng cửa bên ngoài.

Điều này đang sử dụng Python context manager API.

EDIT: Có vẻ như điều này đã tồn tại nhưng là không được chấp nhận: Xem contextlibthis SO question. Sử dụng nó như thế này:

import contextlib 

with contextlib.nested(*[open(fn, 'w') for fn in arr]) as files: 
    print files 
print files 
+0

'contextlib.nested' không được dùng nữa –

+0

Cảm ơn, đã cập nhật. 'ListContext' mà tôi đưa ra ở đây có cùng một sự cẩn thận .. tức là các vấn đề xảy ra nếu' __enter __() 'hoặc' __exit __() 'của các phần tử bên trong kích hoạt các ngoại lệ. – Max

1

Có vẻ như bạn đang về cơ bản tìm kiếm contextlib.nested(), điều này đã phản đối bằng Python 2.7 có lợi cho hình thức quản lý bội số của báo cáo kết quả with nhưng như đã nêu trong các tài liệu:

một lợi thế của chức năng này qua các hình thức quản lý nhiều của với tuyên bố là lập luận giải nén cho phép nó được sử dụng với một số biến của các nhà quản lý bối cảnh

trong trường hợp bạn đang trên Python 3.x, đây là mã từ 2,7 nguồn Python:

from contextlib import contextmanager 

@contextmanager 
def nested(*managers): 
    """Combine multiple context managers into a single nested context manager.                            
    This function has been deprecated in favour of the multiple manager form 
    of the with statement. 

    The one advantage of this function over the multiple manager form of the 
    with statement is that argument unpacking allows it to be 
    used with a variable number of context managers as follows: 

     with nested(*managers): 
      do_something() 

    """ 
    warn("With-statements now directly support multiple context managers", 
     DeprecationWarning, 3)                                         exits = [] 
    vars = [] 
    exc = (None, None, None) 
    try: 
     for mgr in managers: 
      exit = mgr.__exit__ 
      enter = mgr.__enter__ 
      vars.append(enter()) 
      exits.append(exit) 
     yield vars 
    except: 
     exc = sys.exc_info() 
    finally: 
     while exits: 
      exit = exits.pop() 
      try: 
       if exit(*exc): 
        exc = (None, None, None) 
      except: 
       exc = sys.exc_info() 
     if exc != (None, None, None): 
      # Don't rely on sys.exc_info() still containing 
      # the right information. Another exception may 
      # have been raised and caught by an exit method 
      raise exc[0], exc[1], exc[2] 
Các vấn đề liên quan