【Python入门】18.错误处理try...except 与 raise的用法

笔记更新于2019年12月3日,
摘要:错误处理;try...except语句;分析错误信息源头;logging记录错误;raise抛出错误


写在前面:为了更好的学习python,博主记录下自己的学习路程。本学习笔记基于廖雪峰的Python教程,如有侵权,请告知删除。欢迎与博主一起学习Pythonヽ( ̄▽ ̄)ノ


目录

错误处理
try
• except 错误类型
• 无出现错误情况
• 多层调用
分析错误
记录错误
抛出错误
补充

错误处理

在程序运行过程中,可能会遇到各种错误。如原本要输出整数的却输出了字符串,如因用户输入信息有误导致后面无法运行,如从网络抓取数据时网络突然断线导致无法抓取等等。这其中有可以修复的bug,也有无法预测的错误情况。在Python中,有一套内置的错误处理机制,来帮助我们处理错误情况。我们来看看怎么进行错误处理。
try
--
当我们觉得一段代码可能会出错时,就可以用try语句。如果这段代码出错,则直接跳过后面的代码,转到except X语句块,except X是用来捕获错误类型X的。最后可加上finally语句,如果有finally语句,则执行之。

def fn(a):
    try: 
        print('try')
        r = 10 / a
        print('result:', r)
    except ZeroDivisionError as e:
        print('except:', e)
    finally:
        print('finally')
    print('end')
    return

先看一下有错误的情况,当给fn传入0时,会出现除数为0的错误:

>>>fn(0)
try 
except: division by zero 
finally 
end 

分析其执行过程。执行try语句中的“r = 10 / a”发现错误,跳过“print('result:', r)”语句,执行except X语句,捕获到错误类型为ZeroDivisionError,则执行后面紧跟的语句“print('except:', e)”,最后执行finally语句。

• except 错误类型

事实上,Python中的错误也是class,所有的错误类型都继承自BaseException,Python官网给出了常见的错误类型和继承关系。

当可能出现多种错误时,可以加入多个except X语句来捕获不同可能产生的错误。那如果还出现我们能够捕获之外的错误呢?可以在最后添加“except BaseExcept as e:”,或者直接写"except:"也是可以的。不加的话最终会让Python解释器打印出该错误。

def fn(a):
    try: 
        print('try')
        r = 10 / a
        print('result:', r)
    except ZeroDivisionError as e:
        print('except:', e)
    except ValueError as e:
        print('except:', e)
    except :
        print('unknow except')
    finally:
        print('finally')
    print('end')
    return
>>>fn('a')
try 
unknow except 
finally 
end 

当我们尝试传入字符串参数时,发生错误,但错误类型既不是ZeroDivisionError,也不是ValueError,所以最终执行except :语句。最后执行finally语句。若让Python解释器来打印该错误,会发现是TypeError。

• 无出现错误情况

此外,我们可以在所有except后加上else,若没有出现错误,则会执行else语句

看一下没有错误的情况,我们执行fn(2)试试。

def fn(a):
    try: 
        print('try')
        r = 10 / a
        print('result:', r)
    except ZeroDivisionError as e:
        print('except:', e)
    except ValueError as e:
        print('except:', e)
    except :
        print('unknow except')
    else:
        print('no errors')
    finally:
        print('finally')
    print('end')
    return

>>>fn(2)
try 
result: 5.0 
no errors 
finally 
end 

由于没有出现错误,所以执行try里的全部语句,然后跳过except,到else语句。最后执行finally语句。

• 多层调用

try...except语句可以跨越多层调用。比如函数a( )调用了b( ),b( )调用了c( ),c( )出现错误,只要a( )捕获到了就可以处理。

def a(s):
    try:
        b(s)
    except Exception as e:
        print('Error', e)

def b(s):
    return c(s) + 2
def c(s):
    return 9 / s

>>>a(0)
Error division by zero 

分析错误

当错误没有被捕获,它就会一直往上抛,最后被Python解释器捕获,打印错误信息,然后退出程序,如这个err.py:

# err.py:
def a(s):
    b(s)

def b(s):
    return c(s) + 2

def c():
    return 9 / s

a(0)

执行结果:

$ python3 err.py

Traceback (most recent call last):
  File "err.py", line 11, in 
    a(0)
  File "err.py", line 3, in a
    b(s)
  File "err.py", line 6, in b
    return c(s) + 2
  File "err.py", line 9, in c
    return 9 / s
ZeroDivisionError: division by zero

我们从上往下分析这个错误,找出关键错误所在。

错误信息第1行:告诉我们这是错误的跟踪信息。

Traceback (most recent call last):

错误信息第2~3行:表示在文件err.py的第11行代码,模块中的a(0)语句出现错误,但原因是在第3行。

File "err.py", line 11, in 
    a(0)

错误信息第4~5行:表示在文件err.py的第3行代码函数a( )中的b(s)语句出现错误,但原因是在第6行。

File "err.py", line 3, in a
    b(s)

错误信息第6~7行:表示在文件err.py的第6行代码函数b( )中的return c(s) + 2语句出现错误,但原因是在第9行。

  File "err.py", line 6, in b
    return c(s) + 2

错误信息第8~9行:表示在文件err.py的第9行代码函数c( )中的return 9 / s语句出现错误,下面打印出错误原因。

  File "err.py", line 9, in c
    return 9 / s

错误信息第10行:表示出现除数为0的错误,属于ZeroDivisionError错误类型,即计算10 / 0 时出错,找到错误源头。

ZeroDivisionError: division by zero

记录错误

既然可以捕获错误,那么就可以把错误记录下来。

Python内置的logging模块就是用来记录错误的。

import logging
def a(s):
    try:
        b(s)
    except Exception as e:
        logging.exception(e)
        
def b(s):
    return c(s) + 2
def c(s):
    return 9 / s

a(0)
print('end')

执行结果:

ERROR:root:division by zero 
Traceback (most recent call last): 
  File "error.py", line 6, in a 
    b(s) 
  File "error.py", line 11, in b 
    return c(s) + 2 
  File "error.py", line 13, in c 
    return 9 / s 
ZeroDivisionError: division by zero 
end 

最后的print('end')语句是有执行的,可见在有logging的情况下,遇到错误时,程序会打印错误信息后会继续执行,最后正常退出。而没有logging的情况下,遇到错误时会中途退出。

通过配置,logging还可以把错误记录到日志文件里,方便事后排查。

抛出错误

既然错误是class,那我们也可以自己定义一个错误类型,遇到自认为是错误情况时,抛出一个错误实例。用raise语句可以将一个错误实例抛出。

def fn(s):
    n = s
    if n == 0:
        raise MyError('invalid value: %s' % s)
    return 9 / n

fn(0)

执行结果:

Traceback (most recent call last): 
  File "myerror.py", line 11, in  
    fn(0) 
  File "myerror.py", line 8, in fn 
    raise MyError('invalid value: %s' % s) 
__main__.MyError: invalid value: 0 

错误信息最后跟踪到自己定义的错误。

只有在必要的时候才自己定义错误,一般情况下还是用Python内置的错误类型,也是可以用raise语句抛出的。

虽然raise和try语句都有打印错误信息的作用,但两者的用途并不冲突,raise语句负责抛出错误信息,而try语句负责检查是否有错误信息并捕获信息。若没有try语句,那么错误信息就要让python的解释器来处理。

def fn(s):
    n = int(s)
    if n==0:
        raise ValueError('invalid value: %s' % s)
    return 10 / n

def a():
    try:
        fn('0')
    except ValueError as e:
        print('ValueError!')
        raise

a()

raise语句也可以不带参数,此时按原错误信息抛出。此外,在except中raise一个Error,还可以把一种类型的错误转化成另一种类型,像这样:

try:
    10 / 0
except ZeroDivisionError:
    raise ValueError('input error!')

补充

1.split( )函数过指定分隔符对字符串进行切片。传入参数有两个,第一个为分隔符,默认为所有的空字符,包括空格、换行(\n)、制表符(\t)等;第二个为分割次数,默认全部分割。

2.虽然程序可以主动抛出错误让用户知道,但我们要在程序文档中写清楚可能出现的错误以及错误产生的原因和解决办法等。


以上就是本节的全部内容,感谢你的阅读。

下一节内容:19.Python中的调试与测试

有任何问题与想法,欢迎评论与吐槽。

和博主一起学习Python吧( ̄▽ ̄)~*

你可能感兴趣的:(【Python入门】18.错误处理try...except 与 raise的用法)