soup scraping parser find_next_sibling examples beautiful python exception nested try-except

parser - web scraping python



Varios `con`s en` try`s (3)

Tengo varios archivos posibles que pueden contener mis datos; se pueden comprimir de diferentes maneras, así que para abrirlos necesito usar file() , gzip.GzipFile() y otros que también devuelvan un objeto de archivo (que admite la interfaz with ).

Quiero probar cada uno de ellos hasta que uno tenga éxito en la apertura, así que podría hacer algo como

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"

lo que obviamente no es factible en caso de que tenga docenas de posibles variantes de compresión. (El anidamiento se hará cada vez más profundo, el código siempre se parecerá mucho).

¿Hay alguna manera más agradable de deletrear esto?

EDITAR: Si es posible, me gustaría tener el process(f) fuera de la prueba / excepto también para evitar la captura accidental de excepciones planteadas en el process(f) .


¿Funcionaría esto?

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"

Espero que ayude


Escribiría un administrador de contexto personalizado:

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)

O posiblemente solo una función que devuelve un administrador de contexto:

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"


Sí, puedes poner todas tus variantes a través de una lista y probarlas hasta que una de ellas funcione, anulando así tu código:

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

O bien, para reducir la cantidad de código, puede crear una fábrica process_func, ya que todos tienen la misma forma:

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), ]