1. 错误和异常
1.1 错误
Python 中错误一般分为两种:
- 语法错误: 指的是没有按照 Python 语法来写,一般都是书写错误,这种情况解释器一般都能检测到,必须在程序执行前解决。
- 逻辑错误: 指的是代码不符合逻辑而发生的错误,(如:1/0)。
print('hi) # 语法错误
res = 1/0 # 逻辑错误
int('s') # 逻辑错误
1.2 异常
异常是程序在运行时发生错误而产生的信号。
print(hello) # 引发异常的错误
---------------------------------------------------------------------------
NameError Traceback (most recent call last) # 追溯信息
<ipython-input-1-1cd80308eb4c> in <module>()
----> 1 print(hello)
NameError: name 'hello' is not defined # 异常类、异常值
异常包括:
- 追溯信息:
Traceback (most recent call last)
- 异常类:
NameError
- 异常值:
name 'hello' is not defined
2. 异常处理
2.1 什么是异常处理?
Python 解释器检测到错误,触发异常(程序员自己也可以触发如: raise)。程序员自己编写一段特定的代码(与程序逻辑无关),用来捕捉这个异常。
捕捉成功后进入另一个处理逻辑,其目的是使程序不奔溃,—— 这就是异常处理。
2.2 为什么要用异常处理?
程序运行错误,触发异常,导致整个程序在错误处停止,后面的代码不会被执行。用户不会喜欢一款总是奔溃的程序。因此给程序提供一种强大的异常处理机制来增强程序的健壮性和容错性很有必要。
2.3 异常种类
Python 为每一种异常定制了一种类型,一种异常标识一种错误。
类型 | 说明 |
---|---|
AttributeError | 试图访问一个对象不存在的属性,如:A.x,而 A 不存在 x 属性 |
IOError | 输入/输出异常,一般为无法打开文件 |
ImportError | 无法引入模块或包,一般为路径问题或名称错误 |
IndentationError | 语法错误的子类,一般为代码没有正确对齐 |
IndexError | 索引超出序列边界,如:x 只有三个元素,却试图访问 x[5] |
KeyError | 试图访问字典里不存在的键 |
KeyboardInterrupt | Ctrl+C 终止程序引发的,仅在终端有效 |
NameError | 使用一个未定义的变量 |
SyntaxError | 语法错误 |
TypeError | 传入对象类型与要求的不符合 |
UnboundLocalError | 试图访问一个还未被设置的局部变量,一般是有另一个同名的全局变量,导致你以为正在访问它 |
ValueError | 传入一个无效的参数,即使值的类型是正确的 |
2.4 异常处理
Python 提供了一种特定的语法结构来进行异常处理。
try-except
语法结构:
try:
可能会出现错误的代码
except 异常类型: # 还可以写成 except 异常类型 as e (e 为异常值)
触发异常后执行的代码
示例:
try:
s = input('please input a num:')
s = int(s)
except ValueError as e:
print(e)
please input a num:sd
invalid literal for int() with base 10: 'sd' # 异常值
2.5 多分支与万能异常
异常类只能捕捉特定的异常,即在进行异常处理之前,首先需要对异常类型进行判断。若异常类型与判断的类型不一致,则会导致处理失败。
try:
l = [1, 2, 3]
l[5]
except NameError as e:
print(e)
IndexError: list index out of range
上面程序中,指定的异常类型为 NameError
,而实际的异常类型为 IndexError
,捕捉失败。
多分支
异常处理还可以像 if 语句一样,可以多分支判断:
try:
l = [1, 2, 3]
l[5]
except NameError as e:
print(e)
except IndexError as e:
print(e)
list index out of range
万能异常
多分支固然可以进行多次判断,但是一旦遇到我们不知道会发生什么异常的时候,就显得有点捉襟见肘了。而且大量的多分支语句,会使得整个程序可读性很差。
Python 给我们提供了一种万能捕捉异常 Exception
,它可以捕捉任何异常,从而不用担心异常类型错误:
try:
l = [1, 2, 3]
l[5]
except Exception as e:
print(e)
list index out of range
多分支与万能异常结合使用
try:
l = [1, 2, 3]
l[5]
except NameError as e:
print(e)
except IndexError as e:
print(e)
except Exception as e:
print(e)
总结
- 如果无论出现什么异常,都统一丢弃,或使用同一代码逻辑去处理,那么只要用
Exception
即可。 - 如果不同的异常需要定制不同的处理逻辑,那就使用多分支。
2.6 其他异常处理语句
除了 try-except
语句外,还有另外一种异常处理语句 :try-else-finally
。
语法结构:
try:
可能会触发异常的代码块
else:
print('没有异常时执行的代码块')
finally:
print('无论是否有异常,都会执行该段代码,通常是清理工作')
示例:
try:
l = [1, 2, 3]
l[5]
except IndexError as e:
print(e)
except KeyError as e:
print(e)
except Exception as e:
print(e)
else:
print('else 执行')
finally:
print('finally 执行')
list index out of range
finally 执行
2.7 主动触发异常
程序出现错误时,会自动触发异常。也可以通过 raise 语句显示地主动触发异常,一旦执行了 raise 语句,raise 后面的语句将不会被执行。语句结构:raise 异常类()
。
s = 'ab'
try:
if type(s) == str:
raise ValueError('ValueError')
except Exception as e:
print(e)
ValueError
2.9 自定义异常
每种异常都是一个类,那么也可以自定义异常类。需要注意的是:自定义的异常需要继承 BaseException。
class DefineError(BaseException):
def __init__(self, msg):
self.msg = msg
try:
raise DefineError('自定义异常')
except Exception as e:
print(e)
---------------------------------------------------------------------------
DefineError Traceback (most recent call last)
<ipython-input-8-9f72e7503995> in <module>()
4
5 try:
----> 6 raise DefineError('自定义异常')
7 except Exception as e:
8 print(e)
DefineError: 自定义异常
2.9 断言
断言 assert 与 if 条件语句类似,不过它更为简洁。
语法结构:
assert 条件 # 条件符合则继续执行,否则报错
示例:
n = 90
assert n > 85
print('良好')
良好
try...excepy 与 if 的区别
- 两者都有多分支,else 好比 Exception。但是 if 针对一种异常的多分支,对于不同代码同一种类的错误,就需要重复写多分支 if。
- try 是针对不同类型异常的多分支,可以将不同代码放在一起,检测它们的同种类型错误。
2.10 总结
- 使用异常处理可以将错误和工作分开,代码组织更清晰。
- 可以避免一些小的错误导致整个程序奔溃,程序更安全。
3. 什么时候用异常处理
异常处理是附加给程序的一种处理逻辑,与主要工作没有关系。滥用会导致代码可读性差,那么在什么时候可以用呢?
当你遇到一些无法预知的错误时(如:爬取某个站点时爬取超时),就需要用到异常处理来避免程序奔溃。平常情况应该尽量避免少用,尽量减少程序语法以及逻辑性错误。
4. 其他异常类
- BaseException:所有异常基类
- Exception:常见错误基类
- StopIterion:迭代器没有更多的值
- OverflowError:数值运算超出最大限制
- ZeroDivisionError:除(或取模)零
- AssertionError:断言语句失败
- OSError:操作系统错误
- EnvironmentError:操作系统错误的基类
- MemoryError:内存溢出错误(对 Python 解释器不致命)
- UNboundLocalError:访问未初始化的本地变量
- IndentationError:缩进错误
- TabError:Tab 和空格混用
- NotImplementedError:尚未实现的方法
- UnicodeEncodeError:Unicode 编码时错误
- Unicode TranslateError:Unicode 转换时错误