2016-04-12 20 views
1

私には2つの不純な関数 - fとjがあるとします。 jは1つのバッチでアイテムのリストを生成し、fはバッチのリストを生成します。どちらも何かすべきことがあります。リスト内包を持つPythonのネストされたコンテキストマネージャ

私がする必要があるのは、fとjの両方でクリーンアップを行いながら、クライアントコードにアイテムのフラット化されたリストを提供することです。これを行う1つの方法は、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 in it:yield item'''? – user1685095

+0

あなたの明示的なクリーンアップの要件との関連性は不明です。 – Dunes

関連する問題