【问题标题】:nested context managers in python with list comprehensionpython中具有列表理解的嵌套上下文管理器
【发布时间】:2016-04-12 14:14:56
【问题描述】:

假设我有两个不纯函数 - f 和 j。 j 生成一个批次中的项目列表, f 生成一个批次列表。他们俩都有一些清理工作要做。

我需要做的是为客户端代码提供扁平化的项目列表,同时对 f 和 j 进行清理。一种方法是使用带有 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

编辑1。它实际上不起作用,因为它在 j 之前和 j 之后在实际消耗值之前执行。

【问题讨论】:

    标签: python generator list-comprehension contextmanager


    【解决方案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)
    

    【讨论】:

    • 好的,这行得通,有没有机会用一行with j(i) as it: for item in it: yield item解决这个问题?
    • 不确定这与您对显式清理的要求有何关联。
    猜你喜欢
    • 1970-01-01
    • 2021-02-13
    • 2021-11-17
    • 1970-01-01
    • 1970-01-01
    • 2015-06-24
    • 1970-01-01
    • 2013-08-07
    相关资源
    最近更新 更多