一、什么是异常
异常即是一个事件,该事件会在程序执行过程中发生,影响了程序的正常执行。一般情况下,在Python无法正常处理程序时就会发生一个异常。
异常是Python对象,表示一个错误。当Python脚本发生异常时我们需要捕获处理它,否则程序会终止执行。
在python中不同的异常可以用不同的类型(python中统一了类与类型,类型即类)去标识,不同的类对象标识不同的异常,一个异常标识一种错误:
异常名称 | 描述 |
---|---|
BaseException | 所有异常的基类 |
SystemExit | 解释器请求退出 |
KeyboardInterrupt | 用户中断执行(通常是输入^C) |
Exception | 常规错误的基类 |
StopIteration | 迭代器没有更多的值 |
GeneratorExit | 生成器(generator)发生异常来通知退出 |
StandardError | 所有的内建标准异常的基类 |
ArithmeticError | 所有数值计算错误的基类 |
FloatingPointError | 浮点计算错误 |
OverflowError | 数值运算超出最大限制 |
ZeroDivisionError | 除(或取模)零 (所有数据类型) |
AssertionError | 断言语句失败 |
AttributeError | 对象没有这个属性 |
EOFError | 没有内建输入,到达EOF 标记 |
EnvironmentError | 操作系统错误的基类 |
IOError | 输入/输出操作失败 |
OSError | 操作系统错误 |
WindowsError | 系统调用失败 |
ImportError | 导入模块/对象失败 |
LookupError | 无效数据查询的基类 |
IndexError | 序列中没有此索引(index) |
KeyError | 映射中没有这个键 |
MemoryError | 内存溢出错误(对于Python 解释器不是致命的) |
NameError | 未声明/初始化对象 (没有属性) |
UnboundLocalError | 访问未初始化的本地变量 |
ReferenceError | 弱引用(Weak reference)试图访问已经垃圾回收了的对象 |
RuntimeError | 一般的运行时错误 |
NotImplementedError | 尚未实现的方法 |
SyntaxError | Python 语法错误 |
IndentationError | 缩进错误 |
TabError | Tab 和空格混用 |
SystemError | 一般的解释器系统错误 |
TypeError | 对类型无效的操作 |
ValueError | 传入无效的参数 |
UnicodeError | Unicode 相关的错误 |
UnicodeDecodeError | Unicode 解码时的错误 |
UnicodeEncodeError | Unicode 编码时错误 |
UnicodeTranslateError | Unicode 转换时错误 |
Warning | 警告的基类 |
DeprecationWarning | 关于被弃用的特征的警告 |
FutureWarning | 关于构造将来语义会有改变的警告 |
OverflowWarning | 旧的关于自动提升为长整型(long)的警告 |
PendingDeprecationWarning | 关于特性将会被废弃的警告 |
RuntimeWarning | 可疑的运行时行为(runtime behavior)的警告 |
SyntaxWarning | 可疑的语法的警告 |
UserWarning | 用户代码生成的警告 |
二、异常处理
1. 什么是异常处理
- python解释器检测到错误,触发异常(也允许程序员自己触发异常)
- 程序员编写特定的代码,专门用来捕捉这个异常(这段代码与程序逻辑无关,与异常处理有关)
- 如果捕捉成功则进入另外一个处理分支,执行你为其定制的逻辑,使程序不会崩溃,这就是异常处理
2. 为什么要进行异常处理
python解析器去执行程序,检测到了一个错误时,触发异常,异常触发后且没被处理的情况下,程序就在当前异常处终止,后面的代码不会运行,谁会去用一个运行着突然就崩溃的软件。所以你必须提供一种异常处理机制来增强你程序的健壮性与容错性 。
3. 如何进行异常处理
首先须知,异常是由程序的错误引起的,语法上的错误跟异常处理无关,必须在程序运行前就修正
a. 使用if
1 #_*_coding:utf-8_*_ 2 __author__ = 'Linhaifeng' 3 4 #第一段代码 5 num1=input('>>: ') #输入一个字符串试试 6 int(num1) 7 8 9 #第二段代码 10 num2=input('>>: ') #输入一个字符串试试 11 int(num2) 12 13 #第三段代码 14 num3=input('>>: ') #输入一个字符串试试 15 int(num3)
1 #_*_coding:utf-8_*_ 2 __author__ = 'Linhaifeng' 3 4 num1=input('>>: ') #输入一个字符串试试 5 if num1.isdigit(): 6 int(num1) #我们的正统程序放到了这里,其余的都属于异常处理范畴 7 elif num1.isspace(): 8 print('输入的是空格,就执行我这里的逻辑') 9 elif len(num1) == 0: 10 print('输入的是空,就执行我这里的逻辑') 11 else: 12 print('其他情情况,执行我这里的逻辑') 13 14 #第二段代码 15 # num2=input('>>: ') #输入一个字符串试试 16 # int(num2) 17 18 #第三段代码 19 # num3=input('>>: ') #输入一个字符串试试 20 # int(num3) 21 22 ''' 23 问题一: 24 使用if的方式我们只为第一段代码加上了异常处理,针对第二段代码,你得重新写一堆if,elif等 25 第三段,你还得在写一遍,当然了,你说,可以合在一起啊,没错,你合起来看看,你的代码还能被看懂吗??? 26 而这些if,跟你的代码逻辑并无关系,这就好比你在你心爱的程序中到处拉屎,拉到最后,谁都不爱看你的烂代码,为啥,因为可读性差,看不懂 27 28 问题二: 29 第一段代码和第二段代码实际上是同一种异常,都是ValueError,相同的错误按理说只处理一次就可以了,而用if,由于这二者if的条件不同,这只能逼着你重新写一个新的if来处理第二段代码的异常 30 第三段也一样 31 '''
总结:
- if判断式的异常处理只能针对某一段代码,对于不同的代码段的相同类型的错误你需要写重复的if来进行处理。
- 在你的程序中频繁的写与程序本身无关,与异常处理有关的if,就像是在你的代码中到处拉屎。可读性极其的差
- 这是可以解决异常的,只是存在1,2的问题,所以,千万不要妄下定论if不能用来异常处理。如果你不服,那你想想在没有学习try...except之前,你的程序难道就没有异常处理,而任由其崩溃么
b. try/except
- 语法:
1 try: 2 被检测的代码块 3 except 异常类型: 4 try中一旦检测到异常,就执行这个位置的逻辑
- 只能用来处理指定的异常情况,如果非指定异常则无法处理:
1 # 未捕获到异常,程序直接报错 2 3 s1 = 'hello' 4 try: 5 int(s1) 6 except IndexError as e: 7 print e
- 多分支:
1 #形式1 2 3 s1 = 'hello' 4 try: 5 int(s1) 6 except IndexError as e: 7 print(e) 8 except KeyError as e: 9 print(e) 10 except ValueError as e: 11 print(e) 12 13 14 15 #形式2 16 17 try: 18 正常的操作 19 ...................... 20 except(Exception1[, Exception2[,...ExceptionN]]]): 21 发生以上多个异常中的一个,执行这块代码 22 ......................
- 万能异常
1 s1 = 'hello' 2 try: 3 int(s1) 4 except Exception as e: #Exception,可以捕获任意异常 5 print(e)
如果无论出现什么异常,我们统一丢弃,或者使用同一段代码逻辑去处理他们,那么只有一个Exception就足够了。
如果对于不同的异常我们需要定制不同的处理逻辑,那就需要用到多分支了,也可以在多分支后来一个Exception:
1 s1 = 'hello' 2 try: 3 int(s1) 4 except IndexError as e: 5 print(e) 6 except KeyError as e: 7 print(e) 8 except ValueError as e: 9 print(e) 10 except Exception as e: 11 print(e)
- 异常的其它结构:try/except/else/finally
1 s1 = 'hello' 2 try: 3 int(s1) 4 except IndexError as e: 5 print(e) 6 except KeyError as e: 7 print(e) 8 except ValueError as e: 9 print(e) 10 #except Exception as e: 11 # print(e) 12 else: 13 print('try内代码块没有异常则执行我') 14 finally: 15 print('无论异常与否,都会执行该模块,通常是进行清理工作')
- 主动触发异常
1 #_*_coding:utf-8_*_ 2 __author__ = 'Linhaifeng' 3 4 try: 5 raise TypeError('类型错误') 6 except Exception as e: 7 print(e)
- 自定义异常
1 #_*_coding:utf-8_*_ 2 __author__ = 'Linhaifeng' 3 4 class EgonException(BaseException): 5 def __init__(self,msg): 6 self.msg=msg 7 def __str__(self): 8 return self.msg 9 10 try: 11 raise EgonException('类型错误') 12 except EgonException as e: 13 print(e)
- 断言assert
1 # assert 条件 2 3 assert 1 == 1 4 5 assert 1 == 2
- try/except的方式比较if的方式的好处
try/except这种异常处理机制就是取代if那种方式,让你的程序在不牺牲可读性的前提下增强健壮性和容错性
异常处理中为每一个异常定制了异常类型(python中统一了类与类型,类型即类),对于同一种异常,一个except就可以捕捉到,可以同时处理多段代码的异常(无需‘写多个if判断式’)减少了代码,增强了可读性 :
1 #_*_coding:utf-8_*_ 2 __author__ = 'Linhaifeng' 3 4 # num1=input('>>: ') #输入一个字符串试试 5 # if num1.isdigit(): 6 # int(num1) #我们的正统程序放到了这里,其余的都属于异常处理范畴 7 # elif num1.isspace(): 8 # print('输入的是空格,就执行我这里的逻辑') 9 # elif len(num1) == 0: 10 # print('输入的是空,就执行我这里的逻辑') 11 # else: 12 # print('其他情情况,执行我这里的逻辑') 13 14 #第二段代码 15 # num2=input('>>: ') #输入一个字符串试试 16 # int(num2) 17 18 #第三段代码 19 # num3=input('>>: ') #输入一个字符串试试 20 # int(num3) 21 22 try: 23 #第一段代码 24 num1=input('>>: ') #输入一个字符串试试 25 int(num1) #我们的正统程序放到了这里,其余的都属于异常处理范畴 26 #第二段代码 27 num2=input('>>: ') #输入一个字符串试试 28 int(num2) 29 #第三段代码 30 num3=input('>>: ') #输入一个字符串试试 31 int(num3) 32 except ValueError as e: 33 print(e)
使用try..except的方式的好处:
- 把错误处理和真正的工作分开来;
- 代码更易组织,更清晰,复杂的工作任务更容易实现;
- 毫无疑问,更安全了,不至于由于一些小的疏忽而使程序意外崩溃了;
三、何时使用异常处理
try/except应该尽量少用,因为它本身就是你附加给你的程序的一种异常处理的逻辑,与你的主要的工作是没有关系的,这种东西加的多了,会导致你的代码可读性变差
只有在有些异常无法预知的情况下,才应该加上try...except,其他的逻辑错误应该尽量修正。
参考资料: