先来看一小段代码:
try:
print("code started")
raise KeyError
except KeyError as e:
print("key error")
运行结果:
code started
key error
try先打印,然后发生异常,捕获异常,打印。
如果抛的是另外一个异常:
try:
print("code started")
raise IndexError
except KeyError as e:
print("key error")
就会出错:
code started
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-12-d67568d8860d> in <module>
1 try:
2 print("code started")
----> 3 raise IndexError
4 except KeyError as e:
5 print("key error")
IndexError:
如果是预测不到的异常,在python中使用else:
try:
print("code started")
# raise IndexError
except KeyError as e:
print("key error")
else:
print("other error")
运行结果:
code started
other error
把raise IndexError注释掉是因为,如果不注释掉,那么运行到raise IndexError就会直接抛出异常,不会走下面的了。
在try中还有一种用法finally,finally就是不管前面的try和else的有没有运行,都会运行finally中的代码:
try:
print("code started")
# raise IndexError
except KeyError as e:
print("key error")
else:
print("other error")
finally:
print("finally")
运行:
code started
other error
finally
所以,如果我们在try中打开一个文件,使用完这个文件后就需要关闭这个文件:
try:
f_read = open("hellp.txt")
.....
f_read .close()
但是如果在关闭文件之前发生了异常,那么就执行不到关闭文件这一行。
所以在这种情况下,要在捕获异常的代码后面加上close:
try:
f_read = open("hellp.txt")
.....
f_read .close()
except KeyError as e:
print("key error")
f_read .close()
else:
print("other error")
f_read .close()
如果捕获异常还有很多其他的error,还要写很多close:
try:
f_read = open("hellp.txt")
.....
f_read .close()
except KeyError as e:
print("key error")
f_read .close()
except IndexError as e:
print("index error")
f_read .close()
else:
print("other error")
f_read .close()
如果使用finally的话,不管有没有异常都会进入finally,所以就可以直接把close写在finally:
try:
f_read = open("hellp.txt")
.....
f_read .close()
except KeyError as e:
print("key error")
except IndexError as e:
print("index error")
else:
print("other error")
finally:
f_read .close()
再深入理解一下执行过程:
修改一下代码:
def exe_try():
try:
print("code started")
raise KeyError
return 1
except KeyError as e:
print("key error")
return 2
except IndexError as e:
print("index error")
return 3
else:
print("other error")
return 4
finally:
print("finally")
return 5
看一下执行结果:
In [16]: exe_try()
code started
key error
finally
Out[16]: 5
执行顺序:
try print之后发生KeyError异常,这时候不return1,进入except KeyError,但是并没有return 2 是因为,return语句在try——except——finally中的用法是,如果finally有return语句,就return finally中的语句,如果没有,再返回之前调用地方的return语句。
所以如果把finally中的return语句注释掉,那他就会返回2:
In [20]: exe_try()
code started
key error
finally
Out[20]: 2
Python中的with语句简化了try和finally,就是上下文管理器。
上下文管理器协议主要实现了两个魔法函数:__enter__
和__exit__
。
实现了上下文管理器协议的类,就可以被with调用。
也就是说,只要这个类实现了__enter__
和__exit__
,那么这个类就可以叫做上下文管理器。
例如:
class Sample:
def __enter__(self):
print("using enter")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("using exit")
def dosomething(self):
print("doing something")
使用with调用Sample类:
In [22]: with Sample() as s:
...: s.dosomething()
...:
using enter
doing something
using exit
在这个过程中,enter返回什么,with就会给Sample什么。
在with中并没有调用魔法函数,但是with还是自动调用了enter,离开with的时候又调用了exit。
也就是enter时获取资源,exit时释放资源。
还可以更简化:
python提供了一个contextlib包,这个包中有一个装饰器@contextlib.contextmanager
,这个装饰器可以把函数变为一个上下文管理器,这个装饰器修饰的函数必须是一个生成器,也就是用yield返回。
用一小段代码模拟打开文件:
import contextlib
@contextlib.contextmanager
def file_open(file_name):
print("file open")
yield {}
print("file end")
使用with调用一下:
In [24]: with file_open("hello.txt") as f:
...: print("processing")
...:
file open
processing
file end
可以看到执行顺序是,先打开文件,然后执行with中的语句,最后执行关闭文件的print,所以在yield之前的语句相当于enter魔法函数,yield之后的相当于exit魔法函数。
也可以这么使用:
from contextlib import contextmanager
@contextmanager
def file_open(file_name):
print("file open")
yield {}
print("file end")
网友评论