最近我想知道当__exit__
引发异常时不隐式调用__enter__
的背后原因是什么?
为什么这样设计?我正在实施服务运行程序类,以便可以通过'with'
关键字使用,事实证明从未调用过__exit__
。
示例:
class ServiceRunner(object):
def __init__(self, allocation_success):
self.allocation_success = allocation_success
def _allocate_resource(self):
print("Service1 running...")
print("Service2 running...")
# running service3 fails ...
if not self.allocation_success:
raise RuntimeError("Service3 failed!")
print("Service3 running...")
def _free_resource(self):
print("All services freed.")
def __enter__(self):
self._allocate_resource()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self._free_resource()
用法:
with ServiceRunner(allocation_success=True):
pass
try:
with ServiceRunner(allocation_success=False):
pass
except Exception as e:
print(e)
输出:
Service1 running...
Service2 running...
Service3 running...
All services freed.
和
Service1 running...
Service2 running...
Service3 failed!
未调用函数__exit__
。 Service1和Service2不会释放。
我可以将_allocate_resource()
移到__init__
,但是在这种用法中,类不是很有用:
try:
runner = ServiceRunner(allocation_success=True)
except Exception as e:
print(e)
else:
with runner as r:
r.do()
with runner as r:
r.do()
输出:
Service1 running...
Service2 running...
Service3 running...
All services freed.
All services freed.
服务不会再次启动。
我可以重新实现__enter__
来处理异常,但是它向该函数添加了一些样板代码:
def __enter__(self):
try:
self._allocate_resource()
except Exception as e:
self.__exit__(*sys.exc_info())
raise e
这是最好的解决方案吗?
答案 0 :(得分:1)
如果您未能输入上下文,则没有理由尝试退出它,即,如果您未能分配资源,则没有理由尝试释放它。
IIUC,您正在寻找的只是:
try:
with ServiceRunner() as runner:
runner.do()
except Exception as e:
print(e)