欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Python异常处理

程序员文章站 2024-03-18 13:31:34
...

1.异常定义

异常即非正常状态,在Python中使用异常对象来表示异常。若程序在编译或运行过程中发生错误,程序的执行过程就会发生改变,抛出异常对象,程序流进入异常处理。如果异常对象没有被处理或捕捉,程序就会执行回溯(Traceback)来终止程序。

  • 异常的分类:
  1. 程序遇到逻辑或算法错误

  2. 运行过程中计算机错误:内存不够或者io错误

Python异常处理

2.异常处理的定义

python解释器检测到错误,触发异常(也允许程序员自己触发异常)

程序员编写特定的代码,专门用来捕捉这个异常(这段代码与程序逻辑无关,与异常处理有关)

如果捕捉成功则进入另外一个处理分支,执行你为其定制的逻辑,使程序不会崩溃,这就是异常处理

3.异常处理的意义

python解析器去执行程序,检测到了一个错误时,触发异常,异常触发后且没被处理的情况下,程序就在当前异常处终止,后面的代码不会运行,所以你必须提供一种异常处理机制来增强你程序的健壮性与容错性 ,友好性,在程序出现bug时一般不会将错误信息显示给用户,而是出现一个提示的页面。

4.异常处理语法 

(1)try..except..else

注意:except子句的数量没有限制,但使用多个except子句捕获异常时,如果异常类之间具有继承关系,则子类应该写在前面,否则父类将会直接截获子类异常。放在后面的子类异常也就不会执行。 
格式

[python] view plain copy
  1. try:  
  2.     可能触发异常的语句块  
  3. except [exceptionType]:  
  4.     捕获可能触发的异常[可以指定处理的异常类型]  
  5. except [exceptionType][,date]:  
  6.     捕获异常并获取附加数据  
  7. except:  
  8.     没有指定异常类型,捕获任意异常  
  9. else:  
  10.     没有触发异常时,执行的语句块  
try的工作原理: 

执行一个try语句时,python解析器会在当前程序流的上下文中作标记,当出现异常后,程序流能够根据上下文的标记回到标记位,从而避免终止程序。 
1. 如果try语句执行时发生异常,程序流跳回标记位,并向下匹配执行第一个与该异常匹配的except子句,异常处理完后,程序流就通过整个try语句(除非在处理异常时又引发新的异常)。 
2. 如果没有找到与异常匹配的except子句(也可以不指定异常类型或指定同样异常类型Exception,来捕获所有异常),异常被递交到上层的try(若有try嵌套时),甚至会逐层向上提交异常给程序(逐层上升直到能找到匹配的except子句。实在没有找到时,将结束程序,并打印缺省的错误信息)。 
3. 如果在try子句执行时没有发生异常,python将执行else语句后的语句(可选),然后控制流通过整个try语句

需求:将用户输入的两个数字相加

Python异常处理
Python异常处理
while True:
    num1 = raw_input('num1:')
    num2 = raw_input('num2:')
    try:
        num1 = int(num1)
        num2 = int(num2)
        result = num1 + num2
    except Exception, e:
        print '出现异常,信息如下:'
        print e
Python异常处理

(2)try..except..else..finally

try:
    msg = input(">>")
    print(int(msg))
except Exception as e:
    print("异常的类型是:%s"%type(e))
    print("异常的内容是:%s"%e)
else:
    print('如果代码块不抛出异常会执行此行代码!')
finally:
    print('不管代码块是否抛出异常都会执行此行代码!')
  • 运行结果:
>>34
34
如果代码块不抛出异常会执行此行代码!
不管代码块是否抛出异常都会执行此行代码!

(3)万能异常 

在python的异常中,有一个万能异常:Exception,他可以捕获任意异常,即:

1
2
3
4
5
s1 = 'hello'
try:
    int(s1)
except Exception,e:
    print e

既然有这个万能异常,其他异常是不是就可以忽略了???

答:当然不是,对于特殊处理或提醒的异常需要先定义,最后定义Exception来确保程序正常运行。不推荐使用

1
2
3
4
5
6
7
8
9
s1 = 'hello'
try:
    int(s1)
except KeyError,e:
    print '键错误'
except IndexError,e:
    print '索引错误'
except Exception, e:
    print '错误'
捕捉多个异常

方法一:指定一个通用异常,可以捕获多个不同的包含在Exception类中的异常类

  1. try:  
  2.     语句块  
  3. except Exception:  
  4.     语句块  
方法二:在一个except子句后将多个异常作为元组元素列出
  1. try:  
  2.     语句块  
  3. except (IOError,ValueError):   
  4.     语句块  
方法三:except子句后不带任何异常名称,捕获所有异常
  1. try:  
  2.     语句块  
  3. except:   
  4.     语句块  
  1. except语句不是必须的,finally语句也不是必须的,但是二者必须要有一个,否则就没有try的意义了。
  2. except语句可以有多个,Python会按except语句的顺序依次匹配你指定的异常,如果异常已经处理就不会再进入后面的except语句。
  3. except语句可以以元组形式同时指定多个异常,参见实例代码。
  4. except语句后面如果不指定异常类型,则默认捕获所有异常,你可以通过logging或者sys模块获取当前异常。
  5. 如果要捕获异常后要重复抛出,请使用raise,后面不要带任何参数或信息。
  6. 不建议捕获并抛出同一个异常,请考虑重构你的代码。
  7. 不建议在不清楚逻辑的情况下捕获所有异常,有可能你隐藏了很严重的问题。
  8. 尽量使用内置的异常处理语句来替换try/except语句,比如with语句,getattr()方法。

5.Python中的异常处理机制 

在Python当中,若一个程序在运行的时候出错,Python解释器会自动的在出错的地方生成一个异常对象,而后Python解释器会自动的在出错地方的附近寻找有没有对这个异常对象处理的代码,所谓异常处理代码就是try……except语句,如果没有,Python解释器会自动的将这个异常对象抛给其调用函数,就这样层层抛出,如果在main当中也没有对这个异常对象处理的代码,Python解释器(实际上是操作系统)最后会做一个简单粗暴的处理,将整个程序给终止掉,并将错误的信息在显示屏上输出。

6.Python中常见异常种类

python中的异常种类非常多,每个异常专门用于处理某一项异常!!!

Python异常处理
AttributeError 试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x
IOError 输入/输出异常;基本上是无法打开文件在,做文件操作的时候常遇到的错误,这里介绍一下FileNotFoundError 就是做文件操作的时候文件不存在
ImportError 无法引入模块或包;基本上是路径问题或名称错误
IndentationError 语法错误(的子类) ;代码没有正确对齐,缩进问题
IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5]
KeyError 试图访问字典里不存在的键
KeyboardInterrupt Ctrl+C被按下
NameError 使用一个还未被赋予对象的变量
SyntaxError Python 语法错误,少了个冒号啊,没有空格啊,都会出现这个错误。
TypeError 传入对象类型与要求的不符合
UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量,
导致你以为正在访问它
ValueError 传入一个调用者不期望的值,即使值的类型是正确的

python所有的标准异常类:

异常名称 描述
BaseException 所有异常的基类
SystemExit 解释器请求退出
KeyboardInterrupt 用户中断执行(通常是输入^C)
Exception 常规错误的基类
StopIteration 迭代器没有更多的值
GeneratorExit 生成器(generator)发生异常来通知退出
SystemExit Python 解释器请求退出
StandardError 所有的内建标准异常的基类
ArithmeticError 所有数值计算错误的基类
FloatingPointError 浮点计算错误
OverflowError 数值运算超出最大限制
ZeroDivisionError 除(或取模)零 (所有数据类型)在做数据处理和计算的时候会遇到这种错误
AssertionError 断言语句失败
AttributeError 对象没有这个属性
EOFError 没有内建输入,到达EOF 标记
EnvironmentError 操作系统错误的基类
IOError 输入/输出操作失败
OSError 操作系统错误
WindowsError 系统调用失败
ImportError 导入模块/对象失败
KeyboardInterrupt 用户中断执行(通常是输入^C)
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 用户代码生成的警告

7.主动触发异常

如果你需要自主抛出异常一个异常,可以使用raise关键字,等同于C#和Java中的throw,其语法规则如下。

raise NameError("bad name!")格式raise [exceptionType[,argument][,traceback]]
  1. def testRaise(number):  
  2.     if number < 1:  
  3.         raise ValueError('Invalid value'#或者 raise ValueError,'Invalid value'  
  4. testRaise(0)  

raise关键字后面可以指定你要抛出的异常实例,一般来说抛出的异常越详细越好,Python在exceptions模块内建了很多的异常类型,通过使用dir()函数来查看exceptions中的异常类型,如下:

import exceptions

print dir(exceptions)
# ['ArithmeticError', 'AssertionError'...]

当然你也可以查阅Python的文档库进行更详细的了解。

执行raise语句时,python会创建指定异常类的对象,还能够指定对异常对象进行初始化的参数,参数也可以为由若干参数组成的元组。 
注意:一旦执行raise语句,程序就会被终止。 

1
2
3
4

如果输入的数据不是整数,则引发一个ValueError

1
2
3
4
5
inputValue=input("please input a int data :")
if type(inputValue)!=type(1):
    raise ValueError
else:
    print inputValue

假设输入1.2,运行结果为:

please input a int data :1.2
Traceback (most recent call last):
File "C:/Users/lirong/PycharmProjects/untitled/openfile.py", line 3, in <module>
raise ValueError
ValueError

如果输入1,运行结果为:

please input a int data :1
1

8.打印异常信息

Python异常处理
def Mysql_select(db_host,sql):
    try:
        cnx = pymysql.connect(host='192.168.1.15',port= 3306,user = 'root',passwd='123123',db='test',timeout=5)
    # except TypeError:
    #     print 'In typeerror'
    except Exception as e:
        print repr(e)

as获取异常信息

每个异常都会有一定的描述信息,可以通过as关键字来获取。但是这种异常信息并不适合一般用户阅读,所以会使用自定义的异常信息。但是仍然会将原有的异常信息保留起来,用于后期的异常分析

[python] view plain copy
  1. #!/usr/bin/env python  
  2. try:  
  3.     try:  
  4.         openFile = open('notExistsFile.txt','r')  
  5.         fileContent = openFile.readlines()  
  6.     except (IOError,ValueError) as info:  #或者except (IOError,ValueError),info:   
  7.         print info  
  8. except:  
  9.     print 'process exception'  
  10. else:  
  11.     print 'Reading the file'  
output
[python] view plain copy
  1. In [164]: %run testError.py  
  2. [Errno 2] No such file or directory: 'notExistsFile.txt'  
  3. Reading the file  

异常参数

也可以使用异常参数作为输出的异常信息参数,来获取异常信息。并且异常参数中包含有异常信息、错误数字、错误位置等属性

[python] view plain copy
  1. #!/usr/bin/env python  
  2. try:  
  3.     try:  
  4.         openFile = open('notExistsFile.txt','r')  
  5.         fileContent = openFile.readlines()  
  6.     except (IOError,ValueError),info:  
  7.         print dir(info)  
  8.         print info.args  
  9. except:  
  10.     print 'process exception'  
  11. else:  
  12.     print 'Reading the file'  
output
[python] view plain copy
  1. In [44]: %run test.py  
  2. ['__class__''__delattr__''__dict__''__doc__''__format__''__getattribute__''__getitem__''__getslice__''__hash__''__init__''__new__''__reduce__''__reduce_ex__''__repr__''__setattr__''__setstate__''__sizeof__''__str__''__subclasshook__''__unicode__''args''errno''filename''message''strerror']  
  3. (2'No such file or directory')  
  4. Reading the file  

traceback追踪异常

使用traceback追踪异常的时候,需要import traceback模块。traceback模块可以有效的帮助查看异常的详细信息。 
注意:若希望获取异常的详细信息,却又不会终止程序的执行,可以在except子句中使用tarceback.print_exc()函数。 
tarceback.print_exc(): 
print_exc(limit=None, file=None) 
Shorthand for ‘print_exception(sys.exc_type, sys.exc_value, sys.exc_traceback, limit, file)’. 
(In fact, it uses sys.exc_info() to retrieve the same information in a thread-safe way.) 
输出sys.exc_type, sys.exc_value, sys.exc_traceback, limit, file等异常信息,实际上是以线程安全的方式去使用sys.exc_info()函数来获取相同的信息

[python] view plain copy
  1. #!/usr/bin/env python  
  2. import traceback  
  3.   
  4. try:  
  5.     openFile = open('notExistsFile.txt','r')  
  6.     fileContent = openFile.readlines()  
  7. except IOError as info:  
  8.     print 'File not Exists'  
  9.     print info  
  10.     traceback.print_exc()  
  11.     print 'continue'  
  12. except:  
  13.     print 'process exception'  
  14. else:  
  15.     print 'Reading the file'  
output
[python] view plain copy
  1. In [38]: %run test.py  
  2. File not Exists  
  3. [Errno 2] No such file or directory: 'notExistsFile.txt'  
  4. Traceback (most recent call last):  
  5.   File "/usr/local/src/pyScript/test.py", line 5in <module>  
  6.     openFile = open('notExistsFile.txt','r')  
  7. IOError: [Errno 2] No such file or directory: 'notExistsFile.txt'  
  8. continue  
异常信息的重定向:如果希望将异常的信息保存在一个指定的文件中,以供后期分析。可以使用下面的方法
[python] view plain copy
  1. #!/usr/bin/env python  
  2. import traceback  
  3.   
  4. try:  
  5.     with open('notExistsFile.txt','r') as openFile:  
  6.         fileContent = openFile.readlines()  
  7. except IOError:  
  8.     with open('errorLog','w+') as errorInfo:  
  9.         traceback.print_exc(file=errorInfo)  
  10.     print 'continue'  
  11. except:  
  12.     print 'process exception'  
  13. else:  
  14.     print 'Reading the file'  
output
[python] view plain copy
  1. In [61]: %run test.py  
  2. continue  
  3.   
  4. In [62]: cat errorLog  
  5. Traceback (most recent call last):  
  6.   File "/usr/local/src/pyScript/test.py", line 5in <module>  
  7.     with open('notExistsFile.txt','r') as openFile:  
  8. IOError: [Errno 2] No such file or directory: 'notExistsFile.txt'  
sys.exc_info()获取异常信息

traceback.print_exc()函数实际上是call sys.exc_info()

[python] view plain copy
  1. import sys    
  2. try:    
  3.     a=b    
  4.     b=c    
  5. except:    
  6.     info=sys.exc_info()    
  7.     print info[0],":",info[1]    
output
[python] view plain copy
  1. In [65]: %run test.py  
  2. <type 'exceptions.NameError'> : name 'b' is not defined  

1、str(e)

  返回字符串类型,只给出异常信息,不包括异常信息的类型,如1/0的异常信息

  ‘__init__() got an unexpected keyword argument 'timeout'’

2、repr(e)

  给出较全的异常信息,包括异常信息的类型,如1/0的异常信息

  TypeError("__init__() got an unexpected keyword argument 'timeout'",)

3、e.message

  获得的信息同str(e)

4、采用traceback模块

  需要导入traceback模块,此时获取的信息最全,与python命令行运行程序出现错误信息一致。使用traceback.print_exc()打印异常信息到标准错误,就像没有获取一样,或者使用traceback.format_exc()将同样的输出获取为字符串。你可以向这些函数传递各种各样的参数来限制输出,或者重新打印到像文件类型的对象。

9.自定义异常类型

Python中自定义自己的异常类型非常简单,只需要要从Exception类继承即可(直接或间接):

class SomeCustomException(Exception):
    pass
class AnotherException(SomeCustomException):
    pass

一般你在自定义异常类型时,需要考虑的问题应该是这个异常所应用的场景。如果内置异常已经包括了你需要的异常,建议考虑使用内置的异常类型。比如你希望在函数参数错误时抛出一个异常,你可能并不需要定义一个InvalidArgumentError,使用内置的ValueError即可。

自定义的异常类只能通过raise关键字来手动触发

[python] view plain copy
  1. class testError(Exception):    #直接集成Exception类  
  2.     def __init__(self,arg):  
  3.         self.args = arg  
  4. try:  
  5.     raise testError('Just test')  
  6. except testError,info:  
  7.     print info.args  
output
[python] view plain copy
  1. In [52]: %run test.py  
  2. ('J''u''s''t'' ''t''e''s''t')  
class WupeiqiException(Exception): 
    def __init__(self, msg):
        self.message = msg
    def __str__(self):
        return self.message
try:
    raise WupeiqiException('我的异常')
except WupeiqiException,e:
    print e

with..as触发异常自动关闭资源

在使用类文件的流对象时,都需要单独的调用close()来关闭资源。with..as语句能够实现在with语句块执行完后,自动的关闭文件。如果with语句块中触发异常,会调用默认的异常处理器处理,而且文件仍然能够正常关闭

[python] view plain copy
  1. #!/usr/bin/env python  
  2. import os  
  3. def testWith(fileName):  
  4.     try:  
  5.         with open(fileName,'r+') as pwd:  
  6.             pwd.readlines()  
  7.             print 2/0  
  8.     except Exception:  
  9.             print 'File closed:',pwd.closed  #判断文件是否关闭  
  10. if __name__ == '__main__':  
  11.     if os.path.exists('/usr/local/src/pyScript/fileOperation.txt'):  
  12.         testWith('/usr/local/src/pyScript/fileOperation.txt')  
  13.         print 'continue'  
output
[python] view plain copy
  1. In [17]: %run test.py  
  2. File closed: True    #没有call close()函数,文件仍然自动关闭。  
  3. continue  

10.传递异常 re-raise Exception

捕捉到了异常,但是又想重新抛出它(传递异常),使用不带参数的raise语句即可:

def f1():
    print(1/0)

def f2():
    try:
        f1()
    except Exception as e:
        raise  # don't raise e !!!

f2()

在Python2中,为了保持异常的完整信息,那么你捕获后再次抛出时千万不能在raise后面加上异常对象,否则你的trace信息就会从此处截断。以上是最简单的重新抛出异常的做法,也是推荐的做法。

还有一些技巧可以考虑,比如抛出异常前你希望对异常的信息进行更新。

def f2():
    try:
        f1()
    except Exception as e:
        e.args += ('more info',)
        raise

如果你有兴趣了解更多,建议阅读这篇博客。

Python3对重复传递异常有所改进,你可以自己尝试一下,不过建议还是遵循以上规则。

11.Exception 和 BaseException

当我们要捕获一个通用异常时,应该用Exception还是BaseException?我建议你还是看一下 官方文档说明,这两个异常到底有啥区别呢? 请看它们之间的继承关系。

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration...
      +-- StandardError...
      +-- Warning...

Exception的层级结构来看,BaseException是最基础的异常类,Exception继承了它。BaseException除了包含所有的Exception外还包含了SystemExitKeyboardInterruptGeneratorExit三个异常。

由此看来你的程序在捕获所有异常时更应该使用Exception而不是BaseException,因为被排除的三个异常属于更高级别的异常,合理的做法应该是交给Python的解释器处理。

except Exception as e和 except Exception, e

代码示例如下:

try:
    do_something()
except NameError as e:  # should
    pass
except KeyError, e:  # should not
    pass

在Python2的时代,你可以使用以上两种写法中的任意一种。在Python3中你只能使用第一种写法,第二种写法已经不再支持。第一个种写法可读性更好,而且为了程序的兼容性和后期移植的成本,请你果断抛弃第二种写法。

raise "Exception string"

把字符串当成异常抛出看上去是一个非常简洁的办法,但其实是一个非常不好的习惯。

if is_work_done():
    pass
else:
    raise "Work is not done!" # not cool

上面的语句如果抛出异常,那么会是这样的:

Traceback (most recent call last):
  File "/demo/exception_hanlding.py", line 48, in <module>
    raise "Work is not done!"
TypeError: exceptions must be old-style classes or derived from BaseException, not str

这在 Python2.4 以前是可以接受的做法,但是没有指定异常类型有可能会让下游没办法正确捕获并处理这个异常,从而导致你的程序难以维护。简单说,这种写法是是封建时代的陋习,应该扔了。

使用内置的语法范式代替try/except

Python 本身提供了很多的语法范式简化了异常的处理,比如for语句就处理了的StopIteration异常,让你很流畅地写出一个循环。

with语句在打开文件后会自动调用finally并关闭文件。我们在写 Python 代码时应该尽量避免在遇到这种情况时还使用try/except/finally的思维来处理。

# should not
try:
    f = open(a_file)
    do_something(f)
finally:
    f.close()

# should 
with open(a_file) as f:
    do_something(f)

再比如,当我们需要访问一个不确定的属性时,有可能你会写出这样的代码:

try:
    test = Test()
    name = test.name  # not sure if we can get its name
except AttributeError:
    name = 'default'

其实你可以使用更简单的getattr()来达到你的目的。

name = getattr(test, 'name''default')

最佳实践

最佳实践不限于编程语言,只是一些规则和填坑后的收获。

  1. 只处理你知道的异常,避免捕获所有异常然后吞掉它们。
  2. 抛出的异常应该说明原因,有时候你知道异常类型也猜不出所以然。
  3. 避免在catch语句块中干一些没意义的事情,捕获异常也是需要成本的。
  4. 不要使用异常来控制流程,那样你的程序会无比难懂和难维护。
  5. 如果有需要,切记使用finally来释放资源。
  6. 如果有需要,请不要忘记在处理异常后做清理工作或者回滚操作。
相关标签: Python 异常处理