2012-09-24 7 views
17

Ich habe mehrere mögliche Dateien, die meine Daten halten könnten; Sie können auf verschiedene Arten komprimiert werden. Um sie zu öffnen, muss ich file(), gzip.GzipFile() und andere verwenden, die ebenfalls ein Dateiobjekt zurückgeben (das die with Schnittstelle unterstützt).Mehrere `` `` `` `` `` ``

möchte ich jeden von ihnen versuchen, bis man in der Öffnung gelingt, so konnte ich so etwas wie

try: 
    with gzip.GzipFile(fn + '.gz') as f: 
    result = process(f) 
except (IOError, MaybeSomeGzipExceptions): 
    try: 
    with xCompressLib.xCompressFile(fn + '.x') as f: 
     result = process(f) 
    except (IOError, MaybeSomeXCompressExceptions): 
    try: 
     with file(fn) as f: 
     result = process(f) 
    except IOError: 
     result = "some default value" 

tun, was offensichtlich nicht möglich, falls die ich Dutzende von möglichen Kompressionsvarianten haben. (Die Verschachtelung wird immer tiefer und der Code sieht immer sehr ähnlich aus.)

Gibt es einen schöneren Weg dies zu buchstabieren?

EDIT: Wenn möglich, würde ich gerne die process(f) aus dem Versuch/außer auch, um versehentliches Abfangen von Ausnahmen in der process(f) ausgelöst zu vermeiden.

Antwort

7

hilft ich einen benutzerdefinierten Kontext-Manager schreiben würde:

from contextlib import contextmanager 

filetypes = [('.gz', gzip.GzipFile, (IOError, MaybeSomeGzipExceptions)), 
      ('.x', xCompressLib.xCompressFile, (IOError, MaybeSomeXCompressExceptions))] 

@contextmanager 
def open_compressed(fn): 
    f = None 
    try: 
     for ext, cls, exs in filetypes: 
      try: 
       f = cls(fn + ext) 
      except exs: 
       pass 
      else: 
       break 
     yield f 
    finally: 
     if f is not None: 
      f.close() 

with open_compressed(fn) as f: 
    result = "some default value" if f is None else process(f) 

Oder vielleicht nur eine Funktion, die einen Kontext-Manager zurückgibt:

filetypes = [('.gz', gzip.GzipFile, (IOError, MaybeSomeGzipExceptions)), 
      ('.x', xCompressLib.xCompressFile, (IOError, MaybeSomeXCompressExceptions))] 

class UnknownCompressionFormat(Exception): 
    pass 

def open_compressed(fn): 
    for ext, cls, exs in filetypes: 
     try: 
      return cls(fn + ext) 
     except exs: 
      pass 
    raise UnknownCompressionFormat 

try: 
    with open_compressed(fn) as f: 
     result = process(f) 
except UnknownCompressionFormat: 
    result = "some default value" 
+0

Ich denke, ich mag diesen Ansatz noch mehr als der andere. – Alfe

+2

Schön. Ich mag besonders den zweiten Ansatz. Ich würde vorschlagen, dass beide Ansätze eine Ausnahme für UnknownCompressionFormat werfen. –

9

Ja, könnten Sie alle Ihre Varianten durch eine Liste setzen und sie versuchen, bis einer von ihnen arbeitet, so un-Verschachtelung Code:

def process_gzip(fn): 
    with gzip.GzipFile(fn + '.gz') as f: 
     return process(f) 

def process_xlib(fn): 
    with xCompressLib.xCompressFile(fn + '.x') as f: 
     return process(f) 

def process_builtin(fn): 
    with file(fn) as f: 
     return process(f) 

process_funcs = [process_gzip, process_xlib, process_builtin] 

#processing code: 

for process_f in process_funcs: 
    try: 
     result = process_f(fn) 
     break 
    except IOError: 
     #error reading the file, keep going 
     continue 
    except: 
     #processing error, re-raise the exception 
     raise 

Oder Menge an Code reduzieren Sie eine process_func machen könnte Fabrik, da sie alle haben die gleiche Form:

def make_process_func(constructor, filename_transform): 
    with constructor(filename_transform) as f: 
     return process(f) 

process_funcs = [ 
    make_process_func(gzip.GzipFile, lambda fn: fn + '.gz'), 
    make_process_func(xCompressLib.xCompressFile, lambda fn: fn + '.x'), 
    make_process_func(file, lambda fn: fn), 
] 
+0

ich eine EDIT auf meine Frage: Ich Ich möchte den 'Prozess (f)' aus dem Versuch/außer wenn möglich haben. – Alfe

+1

Das Öffnen der Datei verursacht möglicherweise keinen IOError. das Lesen der Datei könnte dies auch tun. also willst du alle Daten auslesen und dann verarbeiten, wenn du IOError vom Verarbeitungsfehler trennen willst, nein? – Claudiu

+1

Das einzige, was ich hier hinzufügen würde, ist, dass Sie wahrscheinlich Tupel zur Hand haben sollten, die die verschiedenen akzeptablen Ausnahmen enthalten. z.B. 'versuchen: ; außer passable_exceptions: pass' – mgilson

5

würde diese Arbeit:

extensions = [('.gz', gzip.GzipFile, (IOError, MaybeSomeGzipExceptions)), 
       ('.x', xCompressLib.xCompressFile, (IOError, MaybeSomeXCompressExceptions))] # and other such entries 
processed = False 
for ext, (compressor, errors) in extensions.iteritems(): 
    try: 
     with compressor(fn+ext) as f: 
      try: 
       result = process(f) 
       processed = True 
       break 
      except: 
       raise 
    except errors: 
     pass 
if not processed: 
    result = "some default value" 

Hoffnung, die

+0

Nochmals, ich habe ein EDIT zu meiner Frage: ist es möglich, das 'process (f)' aus dem try/catch herauszuholen (was eigentlich nur Exceptions im 'with' Zeug fangen sollte)? – Alfe

+0

Das würde ich auch tun, aber es ist möglich, dass die Reihenfolge wichtig ist. – mgilson

+0

Ordnung ist in der Tat, aber mit einer Liste von Tupeln anstelle eines Diktats ist kein Problem. – Alfe

Verwandte Themen