在程序运行过程中难免会遇到各种意想不到的状况出现,我们需要不断完善代码,对可能出现的程序错误要有准备。程序错误一般分为以下几种可能:
- 本来程序就有错误,如:函数功能不完善,数值计算错误,符号错误,等等。这些bug都是需要我们在编写代码之初,就要解决消除的。
- 另外就是用户输入错误
- 还有就是意想不到的错误,比如说:程序运行过程中,内存读取错误等等
我们常见判断错误的方式返回一个错误码,但是python和其他高级语言一样,有一个更好的方法,让程序员来判断错误,即try...except....finally...机制。
2)try
示例代码如下:
try:
print('try')
r=10/0
print('result:',r)
except ZeroDivisionError as e:
print('except:',e)
finally:
print('finally')
print('end')
输出结果如下:
我们可以看到,这里10/0处有错误,所以程序直接从此处跳到了之前预想到的错误情况“ZeroDivisionError”,在except中输出错误情况:division by zero。最后,输出finally。
我们这里把0改成2,输出结果如下:
我们可以看到最后finally还是会输出的。也就是说,finally只要有就会输出。
你可能注意到我在‘预想到’这里标红了,这意味着可能有好多种类的错误可以由except来捕捉。如下:
try:
print('try')
r=10/int('a')
print('result:',r)
except ValueError as e:
print('ValueError:',e)
except ZeroDivisionError as e:
print('except:',e)
finally:
print('finally')
print('end')
输出结果如下:
我们可以看到,我们输入了一个错误的数据类型'a'。except捕捉到错误判断“ValueError”,然后输出finally。
如果没有指定错误发生,可以用else:
try:
print('try')
r=10/1
print('result:',r)
except ValueError as e:
print('ValueError:',e)
except ZeroDivisionError as e:
print('except:',e)
else:
print('No Error')
finally:
print('finally')
print('end')
输出结果如下:
实际上错误也是一个class。所有错误类型都继承来自BaseException,另外except捕获错误是父类错误优先的。意思是,如果同时捕获父类和子类错误,那么except 只会捕获父类错误。如下:
def function():
r = 10 / int('a')
print('result:',r)
try:
function()
except ValueError as e:
print('ValueError')
except UnicodeError as e:
print('UnicodeError')
输出结果如下:
我们可以看到这里只是输出了父类错误,而子类错误丢失。
python的try...except捕获错误还有一个巨大的好处,就是可以跨越多层调用,示例代码如下:
def bar(s):
return fun(s) * 2
def main():
try:
bar('a')
except ValueError as e:
print('Error:', e)
finally:
print('finally...')
if __name__ == '__main__':
main()
输出结果如下:
这里函数main()调用bar(),bar()调用fun(),结果fun()出错了,只要main()捕获到了,就可以输出错误。
ps:廖老师这部分内容有笔误。
3)调用栈
如果错误没有被捕获,它就会一直往上抛,最后被Python解释器捕获,打印一个错误信息,然后程序退出。示例代码如下:
def fun(s):
return 10 / int(s)
def bar(s):
return fun(s) * 2
def main():
bar('0')
main()
输出结果如下:
这里,我们先分析一下错误信息:
第1行告诉我们这是错误的跟踪信息;
第2~3行调用main()出错了,在代码文件.py的第48行代码,但原因是第46行;
第4~5行调用bar('0')出错了,在代码文件.py的第46行代码,但原因是第43行;
第6~7行原因是return foo(s) * 2这个语句出错了,但这还不是最终原因,继续往下看;
第8~9行原因是return 10 / int(s)这个语句出错了,这是错误产生的源头,因为下面打印了:ZeroDivisionError: division by zero。
找到错误!
4)记录错误
根据经验我们程序出现错误时候,程序会在错误处停止。既然我们能让捕捉错误,那么我们就可以打印出错误,另外程序继续执行。这里我们用到logging模块。示例代码如下:
import logging
def fun(s):
return 10 / int(s)
def bar(s):
return fun(s) * 2
def main():
try:
bar('0')
except Exception as e:
logging.exception(e)
main()
print('end')
输出结果如下:
这里我们可以看到虽然有错误,但是程序依然执行完毕,输出end。
5)抛出错误
我们之前说过,其实错误也是class,我们捕获一个错误就是捕获到该class的一个实例。因此错误不是凭空产生的而是有意抛出的,既然这样我们也可以自己定制错误。
我们可以先确定好继承关系,然后定义个错误class,然后用raise抛出一个错误实例。示例代码如下:
class FunError(ValueError):
pass
def foo(s):
n = int(s)
if n==0:
raise FunError('invalid value: %s' % s)
return 10 / n
foo('0')
输出结果如下:
这样就定位到我们自己定制的错误了。
注意:
只有在必要的时候才定义我们自己的错误类型。如果可以选择Python已有的内置的错误类型(比如ValueError,TypeError),尽量使用Python内置的错误类型。
希望有志同道合的小伙伴关注我的公众平台,欢迎您的批评指正,共同交流进步。
在程序运行过程中难免会遇到各种意想不到的状况出现,我们需要不断完善代码,对可能出现的程序错误要有准备。程序错误一般分为以下几种可能:
- 本来程序就有错误,如:函数功能不完善,数值计算错误,符号错误,等等。这些bug都是需要我们在编写代码之初,就要解决消除的。
- 另外就是用户输入错误
- 还有就是意想不到的错误,比如说:程序运行过程中,内存读取错误等等
我们常见判断错误的方式返回一个错误码,但是python和其他高级语言一样,有一个更好的方法,让程序员来判断错误,即try...except....finally...机制。
2)try
示例代码如下:
try:
print('try')
r=10/0
print('result:',r)
except ZeroDivisionError as e:
print('except:',e)
finally:
print('finally')
print('end')
输出结果如下:
我们可以看到,这里10/0处有错误,所以程序直接从此处跳到了之前预想到的错误情况“ZeroDivisionError”,在except中输出错误情况:division by zero。最后,输出finally。
我们这里把0改成2,输出结果如下:
我们可以看到最后finally还是会输出的。也就是说,finally只要有就会输出。
你可能注意到我在‘预想到’这里标红了,这意味着可能有好多种类的错误可以由except来捕捉。如下:
try:
print('try')
r=10/int('a')
print('result:',r)
except ValueError as e:
print('ValueError:',e)
except ZeroDivisionError as e:
print('except:',e)
finally:
print('finally')
print('end')
输出结果如下:
我们可以看到,我们输入了一个错误的数据类型'a'。except捕捉到错误判断“ValueError”,然后输出finally。
如果没有指定错误发生,可以用else:
try:
print('try')
r=10/1
print('result:',r)
except ValueError as e:
print('ValueError:',e)
except ZeroDivisionError as e:
print('except:',e)
else:
print('No Error')
finally:
print('finally')
print('end')
输出结果如下:
实际上错误也是一个class。所有错误类型都继承来自BaseException,另外except捕获错误是父类错误优先的。意思是,如果同时捕获父类和子类错误,那么except 只会捕获父类错误。如下:
def function():
r = 10 / int('a')
print('result:',r)
try:
function()
except ValueError as e:
print('ValueError')
except UnicodeError as e:
print('UnicodeError')
输出结果如下:
我们可以看到这里只是输出了父类错误,而子类错误丢失。
python的try...except捕获错误还有一个巨大的好处,就是可以跨越多层调用,示例代码如下:
def bar(s):
return fun(s) * 2
def main():
try:
bar('a')
except ValueError as e:
print('Error:', e)
finally:
print('finally...')
if __name__ == '__main__':
main()
输出结果如下:
这里函数main()调用bar(),bar()调用fun(),结果fun()出错了,只要main()捕获到了,就可以输出错误。
ps:廖老师这部分内容有笔误。
3)调用栈
如果错误没有被捕获,它就会一直往上抛,最后被Python解释器捕获,打印一个错误信息,然后程序退出。示例代码如下:
def fun(s):
return 10 / int(s)
def bar(s):
return fun(s) * 2
def main():
bar('0')
main()
输出结果如下:
这里,我们先分析一下错误信息:
第1行告诉我们这是错误的跟踪信息;
第2~3行调用main()出错了,在代码文件.py的第48行代码,但原因是第46行;
第4~5行调用bar('0')出错了,在代码文件.py的第46行代码,但原因是第43行;
第6~7行原因是return foo(s) * 2这个语句出错了,但这还不是最终原因,继续往下看;
第8~9行原因是return 10 / int(s)这个语句出错了,这是错误产生的源头,因为下面打印了:ZeroDivisionError: division by zero。
找到错误!
3)记录错误
根据经验我们程序出现错误时候,程序会在错误处停止。既然我们能让捕捉错误,那么我们就可以打印出错误,另外程序继续执行。这里我们用到logging模块。示例代码如下:
import logging
def fun(s):
return 10 / int(s)
def bar(s):
return fun(s) * 2
def main():
try:
bar('0')
except Exception as e:
logging.exception(e)
main()
print('end')
输出结果如下:
这里我们可以看到虽然有错误,但是程序依然执行完毕,输出end。
4)抛出错误
我们之前说过,其实错误也是class,我们捕获一个错误就是捕获到该class的一个实例。因此错误不是凭空产生的而是有意抛出的,既然这样我们也可以自己定制错误。
我们可以先确定好继承关系,然后定义个错误class,然后用raise抛出一个错误实例。示例代码如下:
class FunError(ValueError):
pass
def foo(s):
n = int(s)
if n==0:
raise FunError('invalid value: %s' % s)
return 10 / n
foo('0')
输出结果如下:
这样就定位到我们自己定制的错误了。
注意:
只有在必要的时候才定义我们自己的错误类型。如果可以选择Python已有的内置的错误类型(比如ValueError,TypeError),尽量使用Python内置的错误类型。