python一个try块后接一个或多个finally块_Python * with *语句是否完全等同于try-(例外)-finally块?...

小编典典

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

with EXPR as VAR:

BLOCK

转换为

mgr = (EXPR)

exit = type(mgr).__exit__ # Not calling it yet

value = type(mgr).__enter__(mgr)

exc = True

try:

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 true

finally:

# 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

@contextmanager

def 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

#>>> ZeroDivisionError: division by zero

def bad_cleanup():

print("cleaning")

raise StopIteration

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

2020-12-20

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值