2012-09-24 62 views
17

我有幾個可能的文件可以容納我的數據;他們可以用不同的方式壓縮,所以要打開它們,我需要使用file(),gzip.GzipFile()和其他也返回一個文件對象(支持with接口)的其他。`嘗試`中有```

我想試試它們中的每一個,直到在開放成功,所以我可以做類似

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" 

這顯然不是萬一我有幾十個可能的壓縮變形是可行的。 (嵌套會變得越來越深,代碼看起來非常相似。)

有沒有更好的方法來說明這一點?

編輯:如果可能,我想有process(f)出來的嘗試/除了以及以避免意外捕捉process(f)中引發的異常。

回答

7

我會寫一個自定義的上下文管理器:

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) 

或者,也許只是一個函數返回一個上下文管理器:

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

我覺得我比其他人更喜歡那種方法。 – Alfe

+2

不錯。我特別喜歡第二種方法。我會建議讓這兩種方法爲UnknownCompressionFormat拋出異常。 –

9

是的,你可以把你所有的變種通過名單,並嘗試他們,直到他們的作品之一,因而未嵌套代碼:

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 

或者,減少代碼量,你可以做一個process_func工廠,因爲它們都具有相同的形式:

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

我有一個編輯我的問題:我'如果可能的話,我想讓'process(f)'從try /中除外。 – Alfe

+1

只是打開文件可能不會導致IOError。讀取文件也可以。所以如果你想把IOError從處理錯誤中分離出來,你會想要讀出所有數據然後處理它,不是嗎? – Claudiu

+1

我在這裏添加的唯一的事情是,你應該保留包含各種可接受的異常的元組。例如'嘗試:;除passable_exceptions:pass' – mgilson

5

將這工作:

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" 

希望幫助

+0

再一次,我對我的問題進行了編輯:是否有可能從try/catch中獲得'process(f)'(它應該只是在'with'東西中捕獲異常)? – Alfe

+0

這也是我所要做的,但有可能這個順序可能很重要。 – mgilson

+0

訂單確實重要,但使用元組列表而不是字典是沒有問題的。 – Alfe