在程序运行过程中难免会遇到各种意想不到的状况出现,我们需要不断完善代码,对可能出现的程序错误要有准备。程序错误一般分为以下几种可能:

  • 本来程序就有错误,如:函数功能不完善,数值计算错误,符号错误,等等。这些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')

输出结果如下:

python 高级容错 python容错代码_示例代码

我们可以看到,这里10/0处有错误,所以程序直接从此处跳到了之前预想到的错误情况“ZeroDivisionError”,在except中输出错误情况:division by zero。最后,输出finally。

我们这里把0改成2,输出结果如下:

python 高级容错 python容错代码_错误类型_02

我们可以看到最后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')

输出结果如下:

python 高级容错 python容错代码_错误类型_03

我们可以看到,我们输入了一个错误的数据类型'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')

输出结果如下:

python 高级容错 python容错代码_示例代码_04

实际上错误也是一个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 高级容错 python容错代码_父类_05

我们可以看到这里只是输出了父类错误,而子类错误丢失。

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()

输出结果如下:

python 高级容错 python容错代码_父类_06

这里函数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()

输出结果如下:

python 高级容错 python容错代码_示例代码_07

这里,我们先分析一下错误信息:

第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')

输出结果如下:

python 高级容错 python容错代码_错误类型_08

这里我们可以看到虽然有错误,但是程序依然执行完毕,输出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 高级容错 python容错代码_示例代码_09

这样就定位到我们自己定制的错误了。

注意:


只有在必要的时候才定义我们自己的错误类型。如果可以选择Python已有的内置的错误类型(比如ValueError,TypeError),尽量使用Python内置的错误类型。


希望有志同道合的小伙伴关注我的公众平台,欢迎您的批评指正,共同交流进步。

python 高级容错 python容错代码_父类_10


在程序运行过程中难免会遇到各种意想不到的状况出现,我们需要不断完善代码,对可能出现的程序错误要有准备。程序错误一般分为以下几种可能:

  • 本来程序就有错误,如:函数功能不完善,数值计算错误,符号错误,等等。这些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')

输出结果如下:

python 高级容错 python容错代码_示例代码

我们可以看到,这里10/0处有错误,所以程序直接从此处跳到了之前预想到的错误情况“ZeroDivisionError”,在except中输出错误情况:division by zero。最后,输出finally。

我们这里把0改成2,输出结果如下:

python 高级容错 python容错代码_错误类型_02

我们可以看到最后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')

输出结果如下:

python 高级容错 python容错代码_错误类型_03

我们可以看到,我们输入了一个错误的数据类型'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')

输出结果如下:

python 高级容错 python容错代码_示例代码_04

实际上错误也是一个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 高级容错 python容错代码_父类_05

我们可以看到这里只是输出了父类错误,而子类错误丢失。

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()

输出结果如下:

python 高级容错 python容错代码_父类_06

这里函数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()

输出结果如下:

python 高级容错 python容错代码_示例代码_07

这里,我们先分析一下错误信息:

第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')

输出结果如下:

python 高级容错 python容错代码_错误类型_08

这里我们可以看到虽然有错误,但是程序依然执行完毕,输出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 高级容错 python容错代码_示例代码_09

这样就定位到我们自己定制的错误了。

注意:


只有在必要的时候才定义我们自己的错误类型。如果可以选择Python已有的内置的错误类型(比如ValueError,TypeError),尽量使用Python内置的错误类型。