python 中的嵌套上下文管理器,具有列表理解功能
nested context managers in python with list comprehension
假设我有两个不纯函数 - 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 之前和之后执行。
我想说此时你已经不再需要 @contextmanager
装饰器了,是时候编写你自己的上下文管理器了 class.
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)
假设我有两个不纯函数 - 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 之前和之后执行。
我想说此时你已经不再需要 @contextmanager
装饰器了,是时候编写你自己的上下文管理器了 class.
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)