我将不提及范围,因为它实际上并不十分相关。
根据PEP 343,
with EXPR as VAR: BLOCK
转换为
mgr = (EXPR)exit = type(mgr).__exit__ # Not calling it yetvalue = type(mgr).__enter__(mgr)exc = Truetry: try: VAR = value # only if "as VAR" is present BLOCK except: # The exceptional case is handled here exc = False if not exit(mgr, *sys.exc_info()): raise # The exception is swallowed if exit() returns truefinally: # The normal and non-local-goto cases are handled here if exc: exit(mgr, None, None, None)
如您所见,
type(mgr).__enter__就像您所期望的那样被调用,但是 不在内
try。
type(mgr).__exit__在退出时被调用。唯一的区别是, 如果有异常,则采用
if not exit(mgr,*sys.exc_info())path。
with与
finally子句可以执行的 *** 作不同,这提供了自省和静默错误的能力。
contextmanager没有这个复杂 得多 。只是:
def contextmanager(func): @wraps(func) def helper(*args, **kwds): return _GeneratorContextManager(func, *args, **kwds) return helper
然后看一下有问题的课程:
class _GeneratorContextManager(ContextDecorator): def __init__(self, func, *args, **kwds): self.gen = func(*args, **kwds) def __enter__(self): try: return next(self.gen) except StopIteration: raise RuntimeError("generator didn't yield") from None def __exit__(self, type, value, traceback): if type is None: try: next(self.gen) except StopIteration: return else: raise RuntimeError("generator didn't stop") else: if value is None: value = type() try: self.gen.throw(type, value, traceback) raise RuntimeError("generator didn't stop after throw()") except StopIteration as exc: return exc is not value except: if sys.exc_info()[1] is not value: raise
不重要的代码已被删除。
首先要注意的是,如果存在多个
yield,则此代码将出错。
这不会明显影响控制流程。
考虑一下
__enter__。
try: return next(self.gen)except StopIteration: raise RuntimeError("generator didn't yield") from None
如果上下文管理器写得很好,那将永远不会超出预期。
一个区别是,如果生成器抛出StopIteration
,RuntimeError
将产生不同的错误()。这意味着,
with如果您运行的是完全任意的代码,则行为与正常情况并不完全相同。
考虑一个没有错误的
__exit__:
if type is None: try: next(self.gen) except StopIteration: return else: raise RuntimeError("generator didn't stop")
唯一的区别是和以前一样。如果您的代码抛出
StopIteration,它将影响生成器,因此
contextmanager装饰器将误解它。
这意味着:
from contextlib import contextmanager@contextmanagerdef with_cleanup(func): try: yield finally: func()def good_cleanup(): print("cleaning")with with_cleanup(good_cleanup): print("doing") 1/0#>>> doing#>>> cleaning#>>> Traceback (most recent call last):#>>> File "", line 15, in <module>#>>> ZeroDivisionError: division by zerodef bad_cleanup(): print("cleaning") raise StopIterationwith with_cleanup(bad_cleanup): print("doing") 1/0#>>> doing#>>> cleaning
这不太重要,但是可以。
最后:
else: if value is None: value = type() try: self.gen.throw(type, value, traceback) raise RuntimeError("generator didn't stop after throw()") except StopIteration as exc: return exc is not value except: if sys.exc_info()[1] is not value: raise
这就引起了关于的相同问题
StopIteration,但是有趣的是注意到了最后一部分。
if sys.exc_info()[1] is not value: raise
这意味着,如果未处理异常,则回溯将保持不变。如果已处理但存在 新的 回溯,则将引发该回溯。
这完全符合规格。
TL; DR
with
实际上try...finally
,它比a的功能要强大得多,因为它with
可以进行内省并保持沉默。请当心
StopIteration
,但是您可以使用它@contextmanager
来创建上下文管理器。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)