一、错误处理
使用try except 错误处理机制:
int()
函数可能会抛出ValueError
,所以我们用一个except
捕获ValueError
,用另一个except
捕获ZeroDivisionError
。
此外,如果没有错误发生,可以在except
语句块后面加一个else
,当没有错误发生时,会自动执行else
语句:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
try
:
print
(
'try...'
)
r
=
10
/
int
(
'2'
)
print
(
'result:'
, r)
except
ValueError as e:
print
(
'ValueError:'
, e)
except
ZeroDivisionError as e:
print
(
'ZeroDivisionError:'
, e)
else
:
print
(
'no error!'
)
finally
:
print
(
'finally...'
)
print
(
'END'
)
|
使用try...except
捕获错误还有一个巨大的好处,就是可以跨越多层调用,比如函数main()
调用foo()
,foo()
调用bar()
,结果bar()
出错了,这时,只要main()
捕获到了,就可以处理
记录错误:
使用logging模块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
# err_logging.py
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'
)
|
同样是出错,但程序打印完错误信息后会继续执行,并正常退出
抛出错误:
因为错误是class,捕获一个错误就是捕获到该class的一个实例。因此,错误并不是凭空产生的,而是有意创建并抛出的。Python的内置函数会抛出很多类型的错误,我们自己编写的函数也可以抛出错误。
如果要抛出错误,首先根据需要,可以定义一个错误的class,选择好继承关系,然后,用raise
语句抛出一个错误的实例:
1
2
3
4
5
6
7
8
9
10
11
|
# err_raise.py
class
FooError(ValueError):
pass
def
foo(s):
n
=
int
(s)
if
n
=
=
0
:
raise
FooError(
'invalid value: %s'
%
s)
return
10
/
n
foo(
'0'
)
|
最后,我们来看另一种错误处理的方式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
# err_reraise.py
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()
|
在bar()
函数中,我们明明已经捕获了错误,但是,打印一个ValueError!
后,又把错误通过raise
语句抛出去了,这不有病么?
其实这种错误处理方式不但没病,而且相当常见。捕获错误目的只是记录一下,便于后续追踪。但是,由于当前函数不知道应该怎么处理该错误,所以,最恰当的方式是继续往上抛,让顶层调用者去处理。好比一个员工处理不了一个问题时,就把问题抛给他的老板,如果他的老板也处理不了,就一直往上抛,最终会抛给CEO去处理。
二、调试
断言(assert)
1
2
3
4
5
6
7
|
def
foo(s):
n
=
int
(s)
assert
n !
=
0
,
'n is zero!'
return
10
/
n
def
main():
foo(
'0'
)
|
assert
的意思是,表达式n != 0
应该是True
,否则,根据程序运行的逻辑,后面的代码肯定会出错。
如果断言失败,assert
语句本身就会抛出AssertionError
:
logging模块:
1
2
3
4
5
6
|
import
logging
logging.basicConfig(level
=
logging.INFO)
s
=
'0'
n
=
int
(s)
logging.info(
'n = %d'
%
n)
print
(
10
/
n)
|
这就是logging
的好处,它允许你指定记录信息的级别,有debug
,info
,warning
,error
等几个级别,当我们指定level=INFO
时,logging.debug
就不起作用了。同理,指定level=WARNING
后,debug
和info
就不起作用了。这样一来,你可以放心地输出不同级别的信息,也不用删除,最后统一控制输出哪个级别的信息。
三、测试
一、错误处理
使用try except 错误处理机制:
int()
函数可能会抛出ValueError
,所以我们用一个except
捕获ValueError
,用另一个except
捕获ZeroDivisionError
。
此外,如果没有错误发生,可以在except
语句块后面加一个else
,当没有错误发生时,会自动执行else
语句:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
try
:
print
(
'try...'
)
r
=
10
/
int
(
'2'
)
print
(
'result:'
, r)
except
ValueError as e:
print
(
'ValueError:'
, e)
except
ZeroDivisionError as e:
print
(
'ZeroDivisionError:'
, e)
else
:
print
(
'no error!'
)
finally
:
print
(
'finally...'
)
print
(
'END'
)
|
使用try...except
捕获错误还有一个巨大的好处,就是可以跨越多层调用,比如函数main()
调用foo()
,foo()
调用bar()
,结果bar()
出错了,这时,只要main()
捕获到了,就可以处理
记录错误:
使用logging模块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
# err_logging.py
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'
)
|
同样是出错,但程序打印完错误信息后会继续执行,并正常退出
抛出错误:
因为错误是class,捕获一个错误就是捕获到该class的一个实例。因此,错误并不是凭空产生的,而是有意创建并抛出的。Python的内置函数会抛出很多类型的错误,我们自己编写的函数也可以抛出错误。
如果要抛出错误,首先根据需要,可以定义一个错误的class,选择好继承关系,然后,用raise
语句抛出一个错误的实例:
1
2
3
4
5
6
7
8
9
10
11
|
# err_raise.py
class
FooError(ValueError):
pass
def
foo(s):
n
=
int
(s)
if
n
=
=
0
:
raise
FooError(
'invalid value: %s'
%
s)
return
10
/
n
foo(
'0'
)
|
最后,我们来看另一种错误处理的方式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
# err_reraise.py
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()
|
在bar()
函数中,我们明明已经捕获了错误,但是,打印一个ValueError!
后,又把错误通过raise
语句抛出去了,这不有病么?
其实这种错误处理方式不但没病,而且相当常见。捕获错误目的只是记录一下,便于后续追踪。但是,由于当前函数不知道应该怎么处理该错误,所以,最恰当的方式是继续往上抛,让顶层调用者去处理。好比一个员工处理不了一个问题时,就把问题抛给他的老板,如果他的老板也处理不了,就一直往上抛,最终会抛给CEO去处理。
二、调试
断言(assert)
1
2
3
4
5
6
7
|
def
foo(s):
n
=
int
(s)
assert
n !
=
0
,
'n is zero!'
return
10
/
n
def
main():
foo(
'0'
)
|
assert
的意思是,表达式n != 0
应该是True
,否则,根据程序运行的逻辑,后面的代码肯定会出错。
如果断言失败,assert
语句本身就会抛出AssertionError
:
logging模块:
1
2
3
4
5
6
|
import
logging
logging.basicConfig(level
=
logging.INFO)
s
=
'0'
n
=
int
(s)
logging.info(
'n = %d'
%
n)
print
(
10
/
n)
|
这就是logging
的好处,它允许你指定记录信息的级别,有debug
,info
,warning
,error
等几个级别,当我们指定level=INFO
时,logging.debug
就不起作用了。同理,指定level=WARNING
后,debug
和info
就不起作用了。这样一来,你可以放心地输出不同级别的信息,也不用删除,最后统一控制输出哪个级别的信息。
三、测试