错误和异常
语法错误
语法错误, 也被称为解析错误。比如:
In: while True print('Hello World')
Out:
File "<stdin>", line 1
while True print('Hello World')
^
SyntaxError: invalid syntax
语法分析器指出错误行, 并且在检测到错误的位置前面显示一个小“箭头”。 错误是由箭头指向的标记引起的(或者至少是这么检测的): 这个例子中, 函数 print() 被发现存在错误, 因为它前面少了一个冒号:
。错误会输出文件名和行号, 所以如果是从脚本输入的你就知道去哪里检查错误了。
异常
即使代码在语法上是正确的, 但是执行它的时候可能会引发一些错误。运行期间检测到的错误称为 异常。大多数的异常都不会被程序处理, 会以错误信息的形式展示出来。下面是一个异常的例子:
In: def div(a, b):
... return a / b
In: div(1, 0)
Out:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in div
ZeroDivisionError: division by zero
异常以不同的类型出现,这些类型都作为信息的一部分打印出来, 例如上面这个例子的类型是 ZeroDivisionError
。
错误信息的前面部分显示了异常发生的上下文,并以调用栈的形式显示具体信息。
异常处理
try-except 代码块可以捕获异常, try/except
语句用来检测 try 语句块中的错误, 从而让 except 语句捕获异常信息并处理, 这样异常在捕获后, 只会执行设定的处理方案。
如果不想程序在异常发生时结束, 可以在 try 里面捕获异常。
下面是 try-except 的简单语法:
try:
<语句> # 运行别的代码
except <名字>:
<语句> # 如果在try部份引发了'name'异常
except <名字>, <数据>:
<语句> # 如果引发了'name'异常,获得附加的数据
else:
<语句> # 如果没有异常发生
在上一节(异常)中的代码使用 try-except 这样处理:
In: try:
... div(1, 0)
... except ZeroDivisionError:
... print('除数不能为0!')
Out:
除数不能为0!
还可以捕获多种异常类型, 可以把需要捕获的异常类型放到 tuple(元组) 里面:
In: l = []
In: try:
... l[1]
... except (IndexError, ValueError):
... print('Oops!')
Out:
Oops!
except 可以有多个, 不同的异常使用不同的处理方案:
In: try:
... l[1]
... except IndexError:
... print('index error')
... except ValueError:
... print('value error')
Out:
index error
try-except-else
try-except-else
: 如果 try
代码块执行成功, 没有引发异常, 那么程序会执行 else
代码块中的代码:
In: try:
... div(1, 0)
... except ZeroDivisionError:
... print('除数不能为0!')
... else:
... print('代码成功执行!')
Out:
除数不能为0!
In: try:
... div(1, 2)
... except ZeroDivisionError:
... print('除数不能为0!')
... else:
... print('代码成功执行!')
Out:
0.5
代码执行成功!
try-finally
try-finally
: try-finally 语句无论是否发生异常都将执行 finally 的代码:
In: try:
... div(1, 0)
... finally:
... print('finally')
...
Out:
finally
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "<stdin>", line 2, in div
ZeroDivisionError: division by zero
组合 try 语法
将上面的形式合并起来代码如下:
def div(a, b):
try:
result = a / b
except ZeroDivisionError as e: # as 关键字相当于把捕获到的异常加个别名, 在这里是 e
print(e)
else:
print('result is', result)
finally:
print('executing finally clause')
In: div(1, 0)
Out:
division by zero
executing finally clause
In: div(1, 2)
Out:
result is 0.5
executing finally clause
不能捕获没有 except 的异常
如果没有捕获一个特定的异常, 而这个异常发生了, 程序还是会发生错误, 比如在上面代码的基础上调用 div(a, b):
In: div('1', 2)
Out:
executing finally clause
Traceback (most recent call last):
File "E:/develop/dog_book/demo.py", line 17, in <module>
div('1', 2)
File "E:/develop/dog_book/demo.py", line 3, in div
result = a / b
TypeError: unsupported operand type(s) for /: 'str' and 'int'
使用 except 而不带任何异常类型
except 不捕获特定的异常类, 会表示为捕获全部类型的异常:
In: try:
... div(1, 0)
... except:
... print('除数不能为0!')
Out:
除数不能为0!
这种方法不太可取, 可能会导致一些隐藏的问题, 正确的做法是指定某个或者某些确定的异常。
触发异常
可以使用 raise
语句自己触发异常, raise 语法如下:
raise [Exception [, args [, traceback]]]
# Exception: 异常的类型参数标准异常中的任意一种
# args: 自己提供的异常参数
# 最后一个参数是可选的, 如果存在, 是跟踪异常对象
用户自定义异常
在程序中可以通过创建新的异常类来命名自己的异常, 异常应该是典型的继承自 Exception
类, 通过直接或间接的方式。
class NetworkError(Exception):
pass
In: raise NetworkError('network error') # 主动抛出异常
Out:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
__main__.NetworkError: network error
In: try:
... raise NetworkError('network error')
... except NetworkError:
... print('Catch!')
Out:
Catch!
内置异常多以 Error 结尾, 自定义异常建议:
1.以 Exception 后缀表示可修复异常
2.Error 后缀表示不可修复异常
class RequestException(Exception):
pass
class RequestError(Exception):
pass