深入学习Python中的上下文管理器与else块
前言
本文主要个大家介绍了关于Python上下文管理器与else块的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧。
在开始之前,我们先来看看下面这段话:
最终,上下文管理器可能几乎与子程序(subroutine)本身一样重要。目前,我们只了解了上下文管理器的皮毛……Basic语言有with语句,而且很多语言都有。但是,在各种语言中with语句的作用不同,而且做的都是简单的事,虽然可以避免不断使用点号查找属性,但是不会做事前准备和事后清理。不要觉得名字一样,就意味着作用也一样。with语句是非常了不起的特性。
先做这个,再做那个:if语句之外的else块
这个语言特性不是什么秘密,但却没有得到重视:else子句不仅能在if语句中使用,还能在for、while和try语句中使用。for/else、while/else和try/else的语义关系紧密,不过与if/else差别很大。起初,else这个单词的意思阻碍了我对这些特性的理解,但是最终我习惯了。
else子句的行为如下:
for
仅当for循环运行完毕时(即for循环没有被break语句中止)才运行else块。
while
仅当while循环因为条件为假值而退出时(即while循环没有被break语句中止)才运行else块。
try
仅当try块中没有异常抛出时才运行else块。官方文档(https://docs.python.org/3/reference/compound_stmts.html)还指出:“else子句抛出的异常不会由前面的except子句处理。”
注意:
在所有情况下,如果异常或者return、break或continue语句导致控制权跳到了复合语句的主块之外,else子句也会被跳过。
在这些语句中使用else子句通常能让代码更易于阅读,而且能省去一些麻烦,不用设置控制标志或者添加额外的if语句。
在循环中使用else子句的方式如下述代码片段所示:
foriteminmy_list: ifitem.flavor=='banana': break else: raiseValueError('Nobananaflavorfound!')
一开始,你可能觉得没必要在try/except块中使用else子句。毕竟,在下述代码片段中,只有dangerous_call()不抛出异常,after_call()才会执行,对吧?
try: dangerous_call() after_call() exceptOSError: log('OSError...')
然而,after_call()不应该放在try块中。为了清晰和准确,try块中应该只抛出预期异常的语句。因此,像下面这样写更好:
try: dangerous_call() exceptOSError: log('OSError...') else: after_call()
现在很明确,try块防守的是dangerous_call()可能出现的错误,而不是after_call()。而且很明显,只有try块不抛出异常,才会执行after_call()。
上下文管理器和with块
上下文管理器对象存在的目的是管理with语句,就像迭代器的存在是为了管理for语句一样。
with语句的目的是简化try/finally模式。这种模式用于保证一段代码运行完毕后执行某项操作,即便那段代码由于异常、return语句或sys.exit()调用而中止,也会执行指定的操作。finally子句中的代码通常用于释放重要的资源,或者还原临时变更的状态。
上下文管理器协议包含__enter__和__exit__两个方法。with语句开始运行时,会在上下文管理器对象上调用__enter__方法。with语句运行结束后,会在上下文管理器对象上调用__exit__方法,以此扮演finally子句的角色。