先看一个例子:
>>> def first_example(n):
... return 1 / n
...
>>> first_example(5)
0.2
>>> first_example(0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in first_example
ZeroDivisionError: division by zero
>>>
从例子中得知,n不能为0,否则将会报一个ZeroDivisionError
(除数不能为0)的错误,我们需要对错误进行处理,比如说当发生
的时候我们可以事先约定一个错误代码,这样当以后出错的时候就可
出错的原因。而上面的ZeroDivisionError就是系统内定的一个
错误代码。
通常在python中我们使用,以下格式来定义一个错误代码:
>>> def first_example(n):
... try:
... return 1 / n
... except ZeroDivisionError as error:
... print('错误信息:',error)
...
>>> first_example(1)
1.0
>>> first_example(0)
错误信息: division by zero
>>>
将你需要执行的代码块(该代码块运行时可能出错)放
在try语句里面,将出错类型放在except后面,并给其
赋一个引用变量,下面的print是打印错误信息,前半
部分是自定义字符串。
看第二个例子,函数之间的 错误处理:
>>> def too(n):
... return 1 / n
...
>>> def foo(n):
... return too(n)
...
>>> def second_example(n):
... try:
... print(foo(n))
... except ZeroDivisionError as a:
... print('出错原因:',a)
...
>>> second_example(2)
0.5
>>> second_example(0)
出错原因: division by zero
>>>
我们继续往下看,我们在上面的例子上进行修改:
>>> def too(n):
... return 1 / n
...
>>> def foo(n):
... return too(n) * 3
...
>>> def second_example(n):
... try:
... print(foo(n))
... except ZeroDivisionError as a:
... print('出错原因:',a)
... else:
... print('看看我的出现规则')
...
>>>
>>> second_example(2)
1.5
看看我的出现规则
>>> second_example(0)
出错原因: division by zero
不难看出,else语句块是在没有出错的时候执行
出错的时候就不再执行
我们再进行修改一下 :
>>> def too(n):
... return 1 / n
...
>>> def foo(n):
... return too(n) * 3
...
>>> def second_example(n):
... try:
... print(foo(n))
... except ZeroDivisionError as a:
... print('出错原因:',a)
... finally:
... print('我是finally代码块')
...
>>> second_example(2)
1.5
我是finally代码块
>>> second_example(0)
出错原因: division by zero
我是finally代码块
可以看出,不管代码有没有出错,finally代码块都会执行
我们再增加一点东西:
>>> def too(n):
... return int(n)
...
>>> def foo(s):
... a = s.split('+')
... b = map(too,a)
... return reduce(lambda x, y: x + y , b)
>>> def main(q,w):
... try :
... print(1 / q)
... print(w + '= ',foo(w))
... except ZeroDivisionError as error:
... print('错误信息:',error)
... except ValueError as error:
... print('错误信息:',error)
... finally:
... print('代码执行完毕')
...
>>> main(2,'100 + 23 + 45')
0.5
100 + 23 + 45= 168
代码执行完毕
>>> main(0,'100 + 23 + 45')
错误信息: division by zero
代码执行完毕
>>> main(2,'100 + 23 + 4.5')
0.5
错误信息: invalid literal for int() with base 10: ' 4.5'
代码执行完毕
>>> main(0,'100 + 23 + 4.5')
错误信息: division by zero
代码执行完毕
我们还是将需要执行但可能出错的代码放在try
语句中,但我们有两个except,因为我们会猜测
需要执行的代码可能不止报一个错误。所以我们可
以写多个except
python 错误也是class,所有的错误类型都继承自
BaseException,所以在写except的时候我们应该
注意,如果有多个except,父类错误类型在前面的
except中,子类错误类型在后面的except中,那当
出现子类错误类型 的时候,报的是父类错误类型,
子类错误类型永远不会有执行的机会,举个例子:
>>> def third_example(n):
... try:
... return 1 / n
... except BaseException as a:
... print('BaseException',a)
... except ZeroDivisionError as error:
... print('ZeroDivisionError',error)
...
>>> third_example(0)
BaseException division by zero
所以为了避免这种情况,我们应该把表示范
围小的子类错误类型放前面。
常见的错误类型和继承关系进入这个链接:
常见错误类型和继承关系
错误处理的方式还有一种叫断言(assert):
>>> def odd(n):
... assert n
... return 1 / n
...
>>> def fourth_example(n):
... print(odd(n))
...
>>> fourth_example(2)
0.5
>>> fourth_example(0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in fourth_example
File "<stdin>", line 2, in odd
AssertionError
>>>
断言的语法是,当断言后面的语句为真时,继续往下执行,
当为假时就会报一个AssertionError的错误提示
我们再来看一下assert的升级版本:
>>> def odd(n):
... if n == 0:
... raise ValueError('0不能做除数')
... return 1 / n
...
>>> def fourth_example(n):
... print(odd(n))
>>> fourth_example(2)
0.5
>>> fourth_example(0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in fourth_example
File "<stdin>", line 3, in odd
ValueError: 0不能做除数
我们使用了raise关键字,当判断除数为0时
我们就会执行raise所在的语句,
我们在增加点佐料:
def odd(n):
if n == 0 :
raise ZeroDivisionError('0不能做除数')
return 1 / n
def fourth_example(n):
try:
print(odd(n))
except ZeroDivisionError as a:
print('错误提示:',a)
fourth_example(int(input('请输入一个数字:')))
请输入一个数字:4
0.25
请输入一个数字:0
错误提示: 0不能做除数
我的理解是 raise可以改变错误类型和后面的错误提示的内容
不加raise时的提示信息是:
请输入一个数字:0
错误提示: division by zero
我们再修改一下
def odd(n):
if n == 0 :
raise ZeroDivisionError('0不能做除数')
return 1 / n
def fourth_example(n):
try:
print(odd(n))
except ValueError as a:
print('错误提示:',a)
fourth_example(int(input('请输入一个数字:')))
请输入一个数字:0
Traceback (most recent call last):
File "G:/Pyproject/pachong/pachong.py", line 38, in <module>
fourth_example(int(input('请输入一个数字:')))
File "G:/Pyproject/pachong/pachong.py", line 34, in fourth_example
print(odd(n))
File "G:/Pyproject/pachong/pachong.py", line 30, in odd
raise ZeroDivisionError('0不能做除数')
ZeroDivisionError: 0不能做除数
结果并没有except里面的语句,只有raise
在工作中我们有时候会这么做:
def foo(s):
n = int(s)
if n==0:
raise ValueError('invalid value: %s' % s)
return 10 / n
def bar():
try:
foo('0')
except ValueError as e:
print('ValueError!')
raise
bar()
运行一下:
Traceback (most recent call last):
ValueError!
File "G:/Pyproject/pachong/pachong.py", line 52, in <module>
bar()
File "G:/Pyproject/pachong/pachong.py", line 47, in bar
foo('0')
File "G:/Pyproject/pachong/pachong.py", line 42, in foo
raise ValueError('invalid value: %s' % s)
ValueError: invalid value: 0
我们会捕获错误在抛出错误,捕获错误目的只是记录一下,
便于后续追踪。但是,由于当前函数不知道应该怎么处理该错误,
所以,最恰当的方式是继续往上抛,让顶层调用者去处理。好比
一个员工处理不了一个问题时,就把问题抛给他的老板,如果他
的老板也处理不了,就一直往上抛,最终会抛给CEO去处理。
因为错误是class,捕获一个错误就是捕获到该class的
一个实例。因此,错误并不是凭空产生的,而是有意创
建并抛出的。Python的内置函数会抛出很多类型的错误
,我们自己编写的函数也可以抛出错误。
如果要抛出错误,首先根据需要,可以定义一个错误的
class,选择好继承关系,然后,用raise语句抛出一个错
误的实例:
class FooError(ValueError):
pass
def foo(s):
n = int(s)
if n==0:
raise FooError('invalid value: %s' % s)
return 10 / n
foo('0')
执行一下:
Traceback (most recent call last):
File "err_throw.py", line 11, in <module>
foo('0')
File "err_throw.py", line 8, in foo
raise FooError('invalid value: %s' % s)
__main__.FooError: invalid value: 0
最后我们在说一下记录错误,Python内置的logging
模块可以非常容易地记录错误信息:
import logging
def foo(s):
return 10 / int(s)
def bar(s):
return foo(s) * 2
def main():
try:
bar('0')
except Exception as e:
logging.exception(e)
main()
print('END')
执行一下:
ERROR:root:division by zero
Traceback (most recent call last):
File "err_logging.py", line 13, in main
bar('0')
File "err_logging.py", line 9, in bar
return foo(s) * 2
File "err_logging.py", line 6, in foo
return 10 / int(s)
ZeroDivisionError: division by zero
END
你会发现,错误后面的语句也可以执行。
通过配置,logging还可以把错误记录到日志
文件里,方便事后排查,这些我们以后都会接触。