【发布时间】:2017-04-18 00:23:58
【问题描述】:
我想尝试使用 python with 块将修饰符应用于该块中的操作。但我不确定在协程存在的情况下是否可以明智地做到这一点。
例如,假设我有一个 WithContext 对象,它像这样临时压入堆栈:
class WithContext:
stack = []
def __init__(self, val):
self.val = val
def __enter__(self):
WithContext.stack.append(self.val)
def __exit__(self, exc_type, exc_val, exc_tb):
WithContext.stack.pop()
def do_scoped_contextual_thing():
print(WithContext.stack[-1])
(显然堆栈成员必须是线程本地的,但现在忽略它。)
然后这段代码:
with WithContext("a"):
do_scoped_contextual_thing()
with WithContext("b"):
do_scoped_contextual_thing()
with WithContext("c"):
do_scoped_contextual_thing()
将打印:
a
b
c
但现在假设我有一个协程情况:
def coroutine():
with WithContext("inside"):
yield 1
do_scoped_contextual_thing()
yield 2
with WithContext("outside"):
for e in coroutine():
do_scoped_contextual_thing()
print("got " + str(e))
我想要输出这段代码:
outside
got 1
inside
outside
got 2
但实际上它会输出:
inside
got 1
inside
inside
got 2
外部变为内部是因为协程内部的__enter__ 在堆栈顶部放置了一个值,并且在协程结束之前不会调用__exit__(而不是像你一样不断地进入和退出)进出协程)。
有没有办法解决这个问题?是否有“coroutine-local”变量?
【问题讨论】:
-
没有全局可变状态(
WithContext.stack这里);而是通过它。for e in coroutine(stack): do_scoped_contextual_thing(stack)其中stack是不可变的。 -
你考虑过上下文结果的访问器吗?
with WithContext('outside') as outside:...outside.do_scoped_contextual_thing()之类的东西? -
@Ryan 是的,我知道缺点。我还想尝试一下。
-
好吧,否则这是不可能的,所以这是替代方案......
-
我会注意到,@AustinHastings 的建议是 Python 已经如何处理这个问题。
decimal模块确实允许with作用域上下文(基于线程本地,因此每个线程都有自己的上下文堆栈),但也使其大部分(全部?)API 可从上下文对象本身获得,因此您可以显式使用特定上下文而不替换线程的当前上下文。这里的解决方案是将您的with东西用于非协程使用,而协程使用显式引用其自身上下文的本地状态。