Python * with *语句是否完全等同于try-(例外)-finally块?

Python * with *语句是否完全等同于try-(例外)-finally块?,第1张

Python * with *语句是否完全等同于try-(例外)-finally块?

我将不提及范围,因为它实际上并不十分相关。

根据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
    来创建上下文管理器。



欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5647919.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-16
下一篇 2022-12-16

发表评论

登录后才能评论

评论列表(0条)

保存