2016-04-12 81 views
1

想象一下,我有兩個不純的函數 - f和j。 j在一個批次中生成項目列表,並生成批次列表。他們都有一些清理工作要做。python中的嵌套上下文管理器與列表理解

我需要做的是在爲f和j進行清理時爲客戶端代碼提供扁平的項目列表。一種方法是使用generator與for循環在yield之後進行清理,但我不喜歡這種方式,因爲在這種情況下清理並不是顯式的。

所以我找到了一個辦法做到這一點通過使用包裝函數(在此代碼調用dumb

from contextlib import contextmanager 
from split import chop 
from itertools import chain 
from functools import wraps 

xs = list(chop(3, xrange(9))) 


def dumb(manager): 
    @wraps(manager) 
    def g(*args, **kwargs): 
     with manager(*args, **kwargs) as something: 
      return something 
    return g 

@dumb 
@contextmanager 
def j(index): 
    print('before j') 
    yield xs[index] 
    print('after j') 

@contextmanager 
def f(): 
    print('before f') 
    yield chain.from_iterable(j(i) for i in xrange(len(xs))) 

    print('after f') 

with f() as ns: 
    for x in ns: 
     print(x) 

打印

before f 
before j 
after j 
0 
1 
2 
before j 
after j 
3 
4 
5 
before j 
after j 
6 
7 
8 
after f 

EDIT1。它實際上並不工作,因爲它在j之前和j之後正在進行值實際消耗之前。

回答

1

我會說在這一點上,你已經超過了@contextmanager修飾器,現在是編寫你自己的上下文管理器類的時候了。

from contextlib import contextmanager 

xs = [[0, 1, 2], [3, 4, 5], [6, 7, 8]] 

@contextmanager 
def j(index): 
    """Same as before. This is a simple context manager.""" 
    print("j init") 
    yield xs[index] 
    print("j cleanup") 

def _real_f(the_xs): 
    """Mostly the same as before. However, any share state created in the 
    init phase should be passed as an argument. As an example I pass in xs.""" 
    for i in range(len(the_xs)): 
     # can now use explicit with statements for j 
     with j(i) as it: 
      for item in it: 
       yield item 

class f(object): 
    """f is now replaced by a class that can create contexts for the real 
    f to work with""" 
    def __enter__(self): 
     """Init phase. 
     State required by the real f should be passed as an argument (I 
     pass in xs).""" 
     print("f init") 
     return _real_f(xs) 

    def __exit__(self, exception_type, exception, traceback): 
     """Clean up phase. 
     Called at end of block. With any details of any exception that 
     may have occured in the with block. Return a truthful value to 
     swallow the exception. Can raise your own exception if there is a 
     problem in the clean up phase.""" 
     print("f clean up") 

with f() as it: 
    for item in it: 
     print(item) 
+0

好的,這是行得通的,有沒有機會用j(i)作爲它的一行來解決這個問題:對於item中的item:yield item'''? – user1685095

+0

不確定如何與您的顯式清理要求相關聯。 – Dunes