错误与异常
首先要了解python中的错误和异常是什么? 通常来说,程序中的错误至少包括两种,一种是语法错误,另一种则是异常。
所谓语法错误,也就是你写的代码不符合编程规范,无法被识别与执行,比如下面这个例子:
if name is not None
print(name)
if语句漏掉了冒号,不符合python的语法规范,所以程序就会报错invalid syntax。
而异常则是指程序的语法正确,也可以被执行,但在执行过程中遇到了错误,抛出了异常,比如下面的3个例子:
10 / 0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero
order * 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'order' is not defined
1 + [1, 2]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'list'
它们语法完全正确,但显然,我们不能做除法时让分母为0;也不能使用未定义的变量做运算;而让一个整型和一个列表相加也是不可取的。
于是,当程序运行到这些地方时,就抛出了异常,并且终止运行。例子中的ZeroDivisionError NameError 和TypeError,就是三种常见的异常场景类型。
当然,python中还有很多其他异常类型,比如KeyError是指字典中的键找不到;FileNotFoundError是指发送了读取文件的请求,但相应的文件不存在等等。
如何处理异常
如果执行程序中某处抛出了异常,程序就会被终止并退出。为了让其照样运行下去,通常使用try和except来解决,比如:
try:
s = input('please enter two numbers separated by comma: ')
num1 = int(s.split(',')[0].strip())
num2 = int(s.split(',')[1].strip())
...
except ValueError as err:
print('Value Error: {}'.format(err))
print('continue')
...
这里默认用户以逗号相隔的两个整形数字,将其提取后,做后续的操作(注意input函数会将输入转换为字符串类型)。如果我们输入a,b,程序便会抛出异常invalid literal for int() with base 10:'a',然后跳出try这个block。
由于程序抛出的异常类型是ValueError,和except block所catch的异常类型相匹配,所以except block便会被执行,最终输出Value Error: invalid literal for int() with base 10: 'a',并打印出continue。
please enter two numbers separated by comma: a,b
Value Error: invalid literal for int() with base 10: 'a'
continue
except block只接受与它相匹配的异常类型并执行,如果程序抛出的异常并不匹配,那么程序照样会终止并退出。
还是刚刚那么例子,如果我们只输入1,程序抛出的异常就是IndexError: list index out of range , 与ValueError不匹配,那么except block就不会被执行,程序便会终止并退出(continue不会被打印)。
please enter two numbers separated by comma: 1
IndexError Traceback (most recent call last)
IndexError: list index out of range
不过,很显然,这样强调一种类型的写法有很大的局限性。
其中一种解决方案,是在except block中加入多种异常的类型,比如下面这样的写法:
try:
s = input('please enter two numbers separated by comma: ')
num1 = int(s.split(',')[0].strip())
num2 = int(s.split(',')[1].strip())
...
except (ValueError, IndexError) as err:
print('Error: {}'.format(err))
print('continue')
...
或者第二种写法:
try:
s = input('please enter two numbers separated by comma: ')
num1 = int(s.split(',')[0].strip())
num2 = int(s.split(',')[1].strip())
...
except ValueError as err:
print('Value Error: {}'.format(err))
except IndexError as err:
print('Index Error: {}'.format(err))
print('continue')
...
这样,每次程序执行时,except block中只要有一个exception类型与实际匹配即可。
不过,很多时候,我们很难保证程序覆盖所有的异常类型,所以更通常的做法,是在最后一个except block,声明其处理的异常类型是Exception。 Exception是其他所有非系统异常的基类,能够匹配任意非系统异常。那么这段代码就可以写成写成下面这样:
try:
s = input('please enter two numbers separated by comma: ')
num1 = int(s.split(',')[0].strip())
num2 = int(s.split(',')[1].strip())
...
except ValueError as err:
print('Value Error: {}'.format(err))
except IndexError as err:
print('Index Error: {}'.format(err))
except Exception as err:
print('Other error: {}'.format(err))
print('continue')
...
或者,也可以在except后面省略异常类型,这表示与任意异常相匹配(包扣系统异常等):
try:
s = input('please enter two numbers separated by comma: ')
num1 = int(s.split(',')[0].strip())
num2 = int(s.split(',')[1].strip())
...
except ValueError as err:
print('Value Error: {}'.format(err))
except IndexError as err:
print('Index Error: {}'.format(err))
except:
print('Other error')
print('continue')
...
需要注意,当程序中存在多个except block时,最多只有一个except block会被执行。换句话说,如果多个except声明的异常类型都与实际相匹配,那么只有最前面的except block会被执行,其他则被忽略。
异常处理中,还有一个很常见的用法是finally,经常和try,except放在一起来用。无论发生什么情况,finally block中的语句都会被执行,那怕前面的try和except block中使用了return语句。
一个常见的应用场景,便是文件的读取:
import sys
try:
f = open('file.txt', 'r')
.... # some data processing
except OSError as err:
print('OS error: {}'.format(err))
except:
print('Unexpected error:', sys.exc_info()[0])
finally:
f.close()
这段代码中,try block尝试读取file.txt这个文件,并对其中的数据进行一系列的处理,到最后,无论是读取成功还是读取失败,程序都会执行finally中的语句----关闭这个文件流,确保文件的完整性。因此,在finall中,我们通常都会放一些无论如何都要执行的语句。
用户自定义异常
下面的例子中,创建了自定义的异常类型MyInputError,定义并实现了初始化函数和str函数(直接print时调用):
class MyInputError(Exception):
def __init__(self,value): # 自定义异常类型的初始化
self.value = value
def __str__(self): # 自定义异常类型的string表达形式
return ("{} is invalid input".format(repr(self.value)))
try:
raise MyInputError(1) # 抛出MyInputError这个异常
except MyInputError as err:
print('error: {}'.format(err))
如果执行上述代码块并输出,便会得到下面的结果:
error: 1 is invalid input