我正在尝试编写一个使用其他上下文管理器的上下文管理器,因此客户端不需要知道整个配方,只需要知道我正在呈现的界面。我无法使用@contextmanager
执行此操作 - 如果您被异常中断,yield
调用后的代码将无法执行,因此我需要使用基于类的管理器。
这是一个小例子脚本:
from contextlib import contextmanager
import pprint
d = {}
@contextmanager
def simple(arg, val):
print "enter", arg
d[arg] = val
yield
print "exit", arg
del d[arg]
class compl(object):
def __init__(self, arg, val):
self.arg=arg
self.val=val
def __enter__(self):
with simple("one",1):
with simple("two",2):
print "enter complex", self.arg
d[self.arg] = self.val
def __exit__(self,*args):
print "exit complex", self.arg
del d[self.arg]
print "before"
print d
print ""
with compl("three",3):
print d
print ""
print "after"
print d
print ""
输出这个:
before
{}
enter one
enter two
enter complex three
exit two
exit one
{'three': 3}
exit complex three
after
{}
我希望它输出:
before
{}
enter one
enter two
enter complex three
{'one': 1, 'three': 3, 'two': 2}
exit complex three
exit two
exit one
after
{}
有没有办法告诉基于类的上下文管理器用其他上下文管理器包装自己?
答案 0 :(得分:13)
@contextmanager
def compl(arg, val):
with simple("one",1):
with simple("two",2):
print "enter complex", arg
try:
d[arg] = val
yield
finally:
del d[arg]
print "exit complex", arg
答案 1 :(得分:3)
你写了,"我不能用@contextmanager来做 - 如果你被一个例外中断,那么在yield调用之后代码就不会执行。"如果您有必须运行的代码,则可以将其放在try/finally
块中。
import contextlib
@contextlib.contextmanager
def internal_cm():
try:
print "Entering internal_cm"
yield None
print "Exiting cleanly from internal_cm"
finally:
print "Finally internal_cm"
@contextlib.contextmanager
def external_cm():
with internal_cm() as c:
try:
print "In external_cm_f"
yield [c]
print "Exiting cleanly from external_cm_f"
finally:
print "Finally external_cm_f"
if "__main__" == __name__:
with external_cm() as foo1:
print "Location A"
print
with external_cm() as foo2:
print "Location B"
raise Exception("Some exception occurs!!")
输出:
Entering internal_cm
In external_cm_f
Location A
Exiting cleanly from external_cm_f
Finally external_cm_f
Exiting cleanly from internal_cm
Finally internal_cm
Entering internal_cm
In external_cm_f
Location B
Finally external_cm_f
Finally internal_cm
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Anaconda\lib\site-packages\spyderlib\widgets\externalshell\sitecustomize.py", line 540, in runfile
execfile(filename, namespace)
File "C:\untitled0.py", line 35, in <module>
raise Exception("Some exception occurs!!")
Exception: Some exception occurs!!
答案 2 :(得分:2)
您正在做的事情的问题在于,在with
调用中使用__enter__
时,当您进入包装上下文管理器时,您既可以进入,也可以离开包装的上下文管理器。如果你想编写自己的上下文管理器,当你输入包装器进入包装的上下文管理器,然后在你离开时退出它们,你将不得不手动调用包装的上下文管理器的功能。您可能还需要担心异常安全问题。