如何在Python中处理在另一个上下文管理器中创建的上下文管理器?
示例:假设您的类A
充当上下文管理器,而类B
也充当上下文管理器。但是类B
实例必须实例化并使用类A
的实例。我已经通过了PEP 343,这是我想到的解决方案:
class A(object):
def __enter__(self):
# Acquire some resources here
return self
def __exit__(seplf, exception_type, exception, traceback):
# Release the resources and clean up
pass
class B(object):
def __init__(self):
self.a = A()
def __enter__(self):
# Acquire some resources, but also need to "start" our instance of A
self.a.__enter__()
return self
def __exit__(self, exception_type, exception, traceback):
# Release the resources, and make our instance of A clean up as well
self.a.__exit__(exception_type, exception, traceback)
这是正确的做法吗?或者我错过了一些陷阱?
答案 0 :(得分:5)
如果您可以使用@contextlib.contextmanager
装饰器,您的生活将变得更加容易:
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", c
yield [c]
print "Exiting cleanly from external_cm_f", c
finally:
print "Finally external_cm_f", c
if "__main__" == __name__:
with external_cm():
print "Location A"
print
with external_cm():
print "Location B"
raise Exception("Some exception occurs!!")
答案 1 :(得分:4)
或者,您可以像这样编写代码:
with A() as a:
with B(a) as b:
# your code here
您可能想要尝试的另一个解决方案是:
class A:
def __init__(self):
pass
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
pass
class B(A):
def __init__(self):
super().__init__()
def __enter__(self):
super().__enter__()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
super().__exit__(exc_type, exc_val, exc_tb)
在考虑了您的情况后,这可能是一个更好的解决方案:
class Resource:
def __init__(self, dependency=None):
self.dependency = dependency
# your code here
def __enter__(self):
if self.dependency:
self.dependency.__enter__()
# your code here
return self
def __exit__(self, exc_type, exc_val, exc_tb):
# your code here
if self.dependency:
self.dependency.__exit__(exc_type, exc_val, exc_tb)
我不确定以下实现是否正确,但__exit__
必须正确处理异常。我有点难以想象如何在正确处理异常时递归链接调用。
class Resource:
def __init__(self, dependency=None):
self.dependency = dependency
self.my_init()
def __enter__(self):
if self.dependency:
self.dependency.__enter__()
return self.my_enter()
def __exit__(self, exc_type, exc_val, exc_tb):
suppress = False
try:
suppress = self.my_exit(exc_type, exc_val, exc_tb)
except:
exc_type, exc_val, exc_tb = sys.exc_info()
if suppress:
exc_type = exc_val = exc_tb = None
if self.dependency:
suppress = self.dependeny.__exit__(exc_type, exc_val, exc_tb)
if not supress:
raise exc_val.with_traceback(exc_tb) from None
return suppress
def my_init(self):
pass
def my_enter(self):
pass
def my_exit(self, exc_type, exc_val, exc_tb):
pass
答案 2 :(得分:1)
这里是contextmanager中的手动资源管理的一个示例:外部contextmanager管理内部。
class Inner:
def __enter__(self):
print("<inner>")
return self
def __exit__(self, exc_type, exc_value, traceback):
print("</inner>")
class Outer:
def __init__(self):
self.inner = Inner()
def __enter__(self):
self.inner.__enter__()
try:
#raise RuntimeError("Suppose we fail here")
print("<outer>")
return self
except Exception as e:
self.inner.__exit__(None, None, None)
raise e
def __exit__(self, exc_type, exc_value, traceback):
print("</outer>")
self.inner.__exit__(exc_type, exc_value, traceback)
用法正常:
with Outer() as scope:
#raise RuntimeError("Suppose we fail here")
pass
细心的读者会注意到,内部contextmanager现在变成了毫无意义的木偶木偶(因为我们是手动拉动其线程的)。就是这样。