2013-10-16 5 views
14

Hinweis: Ich bin mir dermit mit/als Kontextmanager

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

Syntax. Das ist eine andere Frage.


Bei einer Liste von Strings file_names ist es eine Möglichkeit, with/as zu öffnen jeden Dateinamen, dass eine einzige Zeile mit verwenden. Etwas wie:

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

was natürlich nicht funktioniert, da es versucht, den Kontextmanager auf einer Liste zu verwenden. Die Länge der Liste kann bis zur Laufzeit nicht bekannt sein, z. B. sys.argv[1:]

+0

Sie Ihren eigenen Kontext-Manager schreiben können. Ist das eine Option? es ist ziemlich einfach. http://docs.python.org/release/2.5.1/ref/context-managers.html –

Antwort

9

Wenn Sie Zugriff auf Python haben 3.3+, gibt es eine Sonderklasse entwickelt, um genau zu diesem Zweck: die ExitStack. Es funktioniert genau wie man erwarten würde:

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

Wie wäre es damit?

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 

Ausgang ist:

[<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>] 

Hinweis, sie sind offen im Innern der mit Kontext und außen geschlossen.

Dies verwendet Python context manager API.

EDIT: Es scheint, wie dies bereits existiert, ist aber veraltet: Siehe contextlib und this SO question. Verwenden Sie es wie folgt aus:

import contextlib 

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

'contextlib.nested' ist veraltet –

+0

Danke, aktualisiert. Der 'ListContext', den ich hier gebe, hat dieselben Vorbehalte. D. H. Probleme treten auf, wenn' __enter __() 'oder' __exit __() 'von inneren Elementen Ausnahmen auslösen. – Max

1

Es klingt wie Sie im Grunde für contextlib.nested() suchen sind, wurde diese als veraltet in Python 2.7 zu Gunsten der mehrere Manager Form der with Aussage aber wie in der Dokumentation festgestellt:

der ein Vorteil dieser Funktion über die mehrere Manager Form des mit Aussage ist, dass Argument auspacken ermöglicht es mit einer variablen Anzahl von Kontext-Manager

im Fall verwendet werden Sie hier ist auf 3.x Python ist der Code aus der 2.7 Quelle 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] 
Verwandte Themen