事情的起因其实是我在学习上下文管理器(context manager)时偶然获得的奇怪的知识点,在python中异常处理是属于比较基础但是非常重要的知识点,之前学习了普通用法try…except…else…finally之后也就没有深究,最多也就是额外学习了一个自定义异常的用法。
贴上普通异常语法:
try:
执行代码
except <可能的ERROR 1> as e:
print(e)
except <可能的ERROR 2> as e:
print(e)
except <可能的ERROR 3> as e:
print(e)
except Exception as e:
# 接住所有错误
print(e)
else:
print('try内没有异常')
finally:
print('无论异常与否,都会执行')
那天碰到了try…finally…的用法,突然两眼一抹黑,因为当时的想法是:
如果连except都没有,不能接住报错,那么try的意义在哪里?
之后自己也一直在尝试各种姿势,也是一直没弄懂,后来在python专家群,请教了几位大佬之后,似乎好像现在已经弄懂了,大家可以在CSDN上搜索python专家门诊加入我们,这里感谢@poqweur和@NBody攻城狮两位群里的大佬答疑解惑,也希望大家能一起加入进来。
关于上下文管理器
这里直接贴代码,因为本笔记主要讲的是try…finally…
class Resource():
def __enter__(self):
print('===connect to resource===')
return self
def __exit__(self, exc_type, exc_val, exc_tb):
# 注意1:在__exit__方法中会自动处理异常
# 注意2:__exit__方法必不可少三个参数,用来处理异常
print('===close resource connection===')
def operate(self):
print('===in operation===')
上下文管理器的作用:
- 可以以一种更加优雅的方式,操作(创建/获取/释放)资源,如文件操作、数据库连接;
- 可以以一种更加优雅的方式,处理异常;
使用类是上下文管理器的一种方法,还有一种是使用contextlib装饰器
import contextlib
@contextlib.contextmanager
def open_func(file_name):
print('open file:', file_name, 'in __enter__')
file_handler = open(file_name, 'r')
yield file_handler
print('close file:', file_name, 'in __exit__')
file_handler.close()
return
with open_func('1.txt') as file_in:
print(file_in.read())
使用装饰器的方法无法接住异常,需要手动使用try语法,当时也是看了这段代码懵逼了。
以下为正文
import contextlib
@contextlib.contextmanager
def open_func(file_name):
print('open file:', file_name, 'in __enter__')
file_handler = open(file_name, 'r')
try:
yield file_handler
finally:
# 注意这里的异常语法中没有except
# 注意return的位置
print('close file:', file_name, 'in __exit__')
file_handler.close()
return
with open_func('1.txt') as file1:
print(file1.read())
1/0 # 常规逻辑错误
以下干货来自群内大佬的教诲:
- finally内的代码不管有无异常发生,都会执行,这是基础和前提
- 如果没有异常发生,在try内的代码执行结束后执行else和finally(如果有else和finally的前提下),这是基础
- 如果有异常发生且被except捕获,那么执行except和finally(如果有finally的前提下),这是基础
- try…except…finally语法中,except不是必需的,也就是可以只有try…finally…
- 如果有异常发生但没被捕获,并且有finally,即try…finally…语句。由于finally它一般用于释放资源,如数据库连接、文件流等,虽然有报错,但是为了完整释放资源,finally还暂时不会返回ERROR,所以如果此时有return直接退出函数,那么错误就来不及被接收,相当于(我个人理解)是在错误被呈现之前直接退出函数(由于finally中return的存在),当然如果finally中没有return,那么就走正常的报错流程。
- 如果try和except和finally语句中都有return,实际只会返回finally中的返回值,finally中有return不仅会覆盖try和except内的返回值,还会掩盖try和except内的异常,就像异常没有发生一样。
最后贴一段小代码,以助记忆
def test():
print('start..')
try:
1/0
return 'TRY RETURN'
except Exception as e:
print('ERROR')
return 'EXCEPT RETURN'
else:
pass
finally:
print('end')
return 'FINALLY RETURN'
if __name__ == '__main__':
print(test())
# start..
# ERROR
# end
# FINALLY RETURN'