2013-04-28 80 views
5

我想採取任意數量的代表嵌套tar檔案的路徑,並對最內層的檔案執行操作。麻煩的是,嵌套可以是任意的,所以我需要的上下文管理器的數量也是任意的。如何嵌套任意數量的Python文件上下文管理器?

舉個例子來說:

ARCHIVE_PATH = "path/to/archive.tar" 

INNER_PATHS = (
    "nested/within/archive/one.tar", 
    "nested/within/archive/two.tar", 
    # Arbitary number of these 
) 

def list_inner_contents(archive_path, inner_paths): 
    with TarFile(archive_path) as tf1: 
     with TarFile(fileobj=tf1.extractfile(inner_paths[0])) as tf2: 
      with TarFile(fileobj=tf2.extractfile(inner_paths[1])) as tf3: 
       # ...arbitary level of these! 
       return tfX.getnames() 

contents = list_inner_contents(ARCHIVE_PATH, INNER_PATHS)) 

我不能使用with語句的nesting syntax,因爲可能有任何數量的水平巢。打開第二時,如果有異常拋出

...使用nested()打開這兩個文件是一個編程錯誤作爲第一個文件不會被及時關閉:我不能使用contextlib.nested因爲文檔說就在那裏文件。

有沒有辦法使用語言結構來做到這一點,還是我需要手動管理我自己的打開文件對象堆棧?

+2

在3.3,你可以使用['contextlib.ExitStack'(HTTP://docs.python。組織/ 3 /庫/ contextlib.html#contextlib.ExitStack)。 – delnan 2013-04-28 09:00:29

+0

@delnan - 我有一個**依賴,使我不使用Python 3:/ – detly 2013-04-28 11:57:25

+0

@delnan很好!我不知道這是在python3.3中添加的。看起來很乾淨的解決方案。 – Bakuriu 2013-04-28 18:35:06

回答

4

對於這種情況,您可以使用遞歸。這種感覺是最自然的情況下(當然如果有在Python無特殊治療方法還):

ARCHIVE_PATH = "path/to/archive.tar" 

INNER_PATHS = [ 
    "nested/within/archive/one.tar", 
    "nested/within/archive/two.tar", 
    # Arbitary number of these 
] 

def list_inner_contents(archive_path, inner_paths): 
    def rec(tf, rest_paths): 
     if not rest_paths: 
      return tf.getnames() 

     with TarFile(fileobj=tf.extractfile(rest_paths[0])) as tf2: 
      return rec(tf2, rest_paths[1:]) 

    with TarFile(archive_path) as tf: 
     try: 
      return rec(tf, inner_paths) 
     except RuntimeError: 
      # We come here in case the inner_paths list is too long 
      # and we go too deeply in the recursion 
      return None 
+1

這似乎是唯一的簡單解決方案。人們可以編寫一個自定義的上下文管理器來手動調用'__enter__'和'__exit__'方法,但是很難處理這些異常以使其表現出預期的效果。 – Bakuriu 2013-04-28 07:06:48