2016-04-12 3 views
1

Представьте, что у меня две нечистые функции - f и j. j генерирует список элементов в одной партии, а f генерирует список партий. У обоих есть какая-то очистка.Вложенные контекстные менеджеры в python с пониманием списка

Что мне нужно сделать, так это предоставить код клиента с помощью сплющенного списка элементов при очистке как для f, так и j. Один из способов сделать это - использовать генератор с циклом, который выполняет очистку после урока, но мне это не нравится, потому что не ясно, что в этом случае происходит очистка.

Так что я нашел способ сделать это с помощью функции-оболочки (так называемый 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'''? – user1685095

+0

Не знаете, как это связано с вашим требованием для явной очистки. – Dunes