Python回顾与整理8:错误和异常

0.说明
python


        若是想写出用户体验高的代码,那么就须要考虑到在执行本身写的这段代码中在和用户交互的过程当中可能会出现的问题,也就是说,须要对可能出现的异常进行处理,只有作好这些工做,才能写出用户体验好的代码。数据库




1.什么是异常express


  • 错误编程

        错误是语法(致使解释器没法解释)或逻辑(也就是代码质量问题)上的,在Python中,当检测到错误时,解释器会指出当前流没法继续执行下去,因而就出现了异常。bash

  • 异常app

        程序出现了错误而在正常控制流之外采起的行为。
python2.7

        根据上面的解释,能够理解为,只要解释器检测到程序运行时出现了错误(与Python解释器不相容而致使),就会触发一个异常。
ide




2.Python中的异常函数


        以下:
工具

异常类型 描述 简单例子
NameError 尝试访问一个未声明的变量,或者是在名称空间中不存在的变量
>>> xpleaf
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'xpleaf' is not defined
ZeroDivisionError 除数为零
>>> 1/0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division
 or modulo by zero
SyntaxError

Python解释器语法错误

(惟一不是在运行时发生的异常,发生在编译时,Python解释器没法把相关脚本编译为Python字节代码)

>>> for
  File "<stdin>", line 1
    for
      ^
SyntaxError: invalid syntax
IndexError 请求的索引走出序列范围
>>> aList = []
>>> aList[0]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range
KeyError 请求一个不存在的字典关键字
>>> aDict = {'name': 'xpleaf', 'love': 'cl'}
>>> aDict['clyyh']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'clyyh'
IOError

输入/输出错误

(任何类型的I/O错误都会引起IOError异常)

>>> f = open('xpleaf')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IOError: [Errno 2] No such file or 
directory: 'xpleaf'
AttributeError 尝试访问未知的对象属性
>>> class myClass(object):
...   pass
... 
>>> myInst = myClass()
>>> myInst.name
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'myClass' object has no 
attribute 'name'




3.检测和处理异常


        须要注意的是,这和前面提到的检测和处理错误并不同,检测和处理错误的结果是会引起一个异常,这是由Python解释器完成的;固然咱们也能够人为地触发一个异常,这时开发者会认为,用户对程序的使用是不正确的,因此才引起这样一个异常。

        当异常出现的时候,若是不对该异常进行处理,那么Python解释器就会停止当前程序的运行,所以,咱们须要对异常进行处理,以达到即便异常出现了,也不会停止程序的执行。


(1)try-except语句

  • 语法

try:
    try_suite    #监测这里的异常
except Exception[, reason]:
    except_suit    #异常处理代码

        reason是错误缘由,由捕获的异常自己带有,只须要定义一个变量便可以对其进行使用。

        打开一个不存在的文件时:

>>> f = open('xpleaf')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IOError: [Errno 2] No such file or directory: 'xpleaf'

        其中:

[Errno 2] No such file or directory: 'xpleaf'

        即是错误缘由,可使用try-except语句来处理上面的异常:

>>> try:
...   f = open('xpleaf', 'r')
... except IOError, e:
...   print 'could not open file:', e
... 
could not open file: [Errno 2] No such file or directory: 'xpleaf'

忽略代码,继续执行,向上移交:

指的是,若是该层代码(好比一个函数内)有相关的异常处理器(即except语句),就会跳到该异常处理器中进行处理,后面的代码会被忽略(后面的其它except语句);若是在该层没有找到对应的异常处理器,该异常会被向上移交,好比移交到调用该函数的上层代码;当异常到达最顶层仍然没有找到对应处理器时,就认为这个异常是未处理的,Python解释器会显示出跟踪记录,而后退出。


(2)带有多个except的try语句

  • 语法

try:
    try_suite
except Exception1[, reason1]:
    suite_for_exception_Exception1
except Exception2[, reason2]:
    suite_for_exception_Exception2

        须要注意的是,当有异常发生时,一旦找到对应的异常处理器,程序的执行流就会跳转到该异常处理器中,其它的except语句将会被忽略。


(3)处理多个异常的except语句

  • 语法

try:
    try_suite
except (Exception1, Exception2)[, reason1]:
    suite_for_exception_Exception1_and_Exception2

        须要注意的是,这些不一样的异常应该被放入到一个元组中。

拓展:包装内建函数

以下:

>>> def safe_float(obj):
...   try:
...     retval = float(obj)
...   except (ValueError, TypeError):
...     retval = 'argument must be a number or numeric string'
...   return retval

执行以下:

>>> safe_float(123)
123.0
>>> safe_float('123')
123.0
>>> safe_float('foo')
'argument must be a number or numeric string'

这是一种很是不错的技巧,要善于利用。


(4)捕获全部异常


        若是须要捕获全部因错误而引发的异常,能够直接捕获Exception异常,Exception是绝大多数Python内建异常的基类。

        可是对于SystemExit和KeyboardInterupt这两个异常,使用Exception是没法捕获的,由于它们不是Exception的继承者,缘由很简单,由于这两个异常不是因为错误条件引发的。SystemExit是因为当前Python应用程序须要退出,KeyboardInterrupt表明用户按下了ctrl-c,想要关闭Python。

        可是这三者都有一个共同的基类,那就是BaseException,也就是这三者在程序结构上是同级的,以下:

BaseException
  -KeyboardInterrupt
  -SystemExit
  -Exception
    -(all other current built-in exceptions)

        所以,若是真的想要捕获全部的异常(包括非错误条件引发的),就可使用BaseException,能够看下面的例子:

  • 使用Exception:没法捕获KeyboardInterrupt

        代码以下:

try:
    name = raw_input('Your name:')
except Exception:
    print 'quit'

        执行以下:

/usr/bin/python2.7 /home/xpleaf/PycharmProjects/Python_book/10/test.py
Your name:Traceback (most recent call last):
  File "/home/xpleaf/PycharmProjects/Python_book/10/test.py", line 3, in <module>
    name = raw_input('Your name:')
KeyboardInterrupt
  • 使用BaseException:捕获全部异常(错误与非错误条件引发的)

        代码以下:

try:
    name = raw_input('Your name:')
except BaseException:
    print 'quit'

        执行以下:

/usr/bin/python2.7 /home/xpleaf/PycharmProjects/Python_book/10/test.py
Your name:quit

        这样的好处是,若是须要同时捕获三个同级的异常,使用一个except语句就能够。


        可是须要注意的是,try-except语句是为了更好地跟踪潜在的错误并在代码里准备好处理异常的逻辑,不该该将其做为异常过滤器来捕获全部异常,并忽略掉这些异常。


(5)异常参数

        其实所谓异常参数,对于前面的一个例子,为何使用e错误缘由时,就能够获得与该异常相关的字符串信息呢?那是由于,异常引起后,它传递了一个参数给异常处理器。

        直接看下面一个例子:

>>> try:
...     float('foo')
... except ValueError, e:
...     print 'Error Happen:', e
... 
Error Happen: could not convert string to float: foo
>>> 
>>> type(e)
<type 'exceptions.ValueError'>
>>> str(e)
'could not convert string to float: foo'
>>> print e
could not convert string to float: foo
>>> e.__class__
<type 'exceptions.ValueError'>
>>> e.__class__.__name__
'ValueError'
>>> e
ValueError('could not convert string to float: foo',)

        咱们能够得出下面的结论:

  • 异常引起时,若是使用错误缘由变量,实际上,这是一个包含来自致使异常的诊断信息的类实例,异常参数自身会组成一个元组,并存储为这个异常类的属性

        在这个例子中的分析是,引起了ValueError异常,而后e就是该异常的一个实例,而且在生成这个实例e的过程当中,异常参数('could not convert string to float: foo',)(注意这是一个元组),就会成为e的一个属性,而使用str(e)能够输出诊断信息的字符串,那是由于调用了该类实例的__str__()方法 。

        注意,若是用一个except语句来同时捕获多个异常时,使用一个错误缘由便可,由于每个异常都会生成本身的异常参数。

        再强调:

  • 异常参数是该异常发生时传递给异常处理器的一个字符串对象,它会成为这个异常类的实例的一个属性,而且能够经过调用str()来得到该诊断信息(使用print语句,实际也是调用了该str()方法)

拓展:继续前面的float()例子

代码以下:

def safe_float(object):
    try:
        retval = float(object)
    except (ValueError, TypeError), diag:
        retval = str(diag)
    return retval

result = safe_float('foo')
print result
result2 = safe_float([])
print result2

执行以下:

/usr/bin/python2.7 /home/xpleaf/PycharmProjects/Python_book/10/test.py
could not convert string to float: foo
float() argument must be a string or a number

        PS:更进一步学习,能够考虑参考异常类的源代码。


(6)else子句

        没有捕获到异常时,就执行else子句中的代码块,一个简单的例子以下:

>>> try:
...     float(4)
... except (ValueError, TypeError), e:
...     print 'Error Happen:', e
... else:
...     print 'No Exceptions'
... 
4.0
No Exceptions


(7)finally子句

        即不管异常是否有发生或是否捕捉到异常,都会执行的语句块。

        经常使用的方式以下:

  • try-except-finally

try:
    A
except Exception1, e:
    B
finally:
    C
  • try-except-else-finally

try:
    A
except Exception1, e:
    B
else:
    C
finally:
    D

        至于书本上说的各类形式上的问题,则能够不用考虑太多,在实践中使用时加以使用把可能出现的状况考虑到就能够了。




4.上下文管理


        try-except和try-finally的一种特定的用法是保证共享的资源的惟一分配,并在任务结束的时候释放它,好比文件、线程资源、简单同步、数据库链接等,以打开文件为例。但其实若是用with语句,会方便不少:

>>> with open('xpleaf.txt', 'r') as f:
...     for eachLine in f:
...         print eachLine
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IOError: [Errno 2] No such file or directory: 'xpleaf.txt'

        with语句帮咱们作了不少事情:试图打开一个文件,若是一切正常,把文件对象赋值给f.而后用迭代器遍历文件中的每一行,当完成时,关闭文件,不管在这一段代码的开始、中间仍是结束时发生异常,会执行清理的代码,此外文件仍会被自动的关闭。

        固然这种方法仅适用于支持上下文管理协议的对象。关于上下文管理协议,因为目前尚未使用到,因此暂不作总结。




5.字符串做为异常


        知道有这种状况就能够,在实际中仍然使用类异常。




6.触发异常


        使用raise关键字就能够人为地触发各类异常。

  • 语法

raise [SomeException [, args [, traceback]]]

        其用法能够有以下:

raise语句的用法
raise语法 描述
raise exclass 触发一个异常,从cxclass生成一个实例(不含任何异常参数)
raise exclass() 同上,但如今不是类;经过函数调用操做符(其实就是指加上了`()`)做用于类生成一个新的exclass实例,一样也没有异常参数
raise exclass, args 同上,但同时提供的异常参数args,能够是一个参数也能够是元组
raise exclass(args) 同上
raise exclass, args, tb 同上,但提供一个跟踪记录(traceback)对象tb供使用
raise exclass, instance 经过实例触发异常(一般是exclass的实例);若是实例是exclass的子类实例,那么这个新异常的类型会是子类的类型(而不是exclass);若是实例既不是exclass的实例也不是exclass子类的实例,那么会复制此实例为异常参数去生成一个新的exclass实例
raise instance
经过实例触发异常:异常类型是实例的类型;等价于raise instance.__class__, instance(同上)
raise 从新触发前一个异常,若是以前没有异常,触发TypeError

        对于raise string以及相关的方法,这里就不说起了,由于实际上不多用到,另外对于traceback也不经常使用。可举例以下:

  • raise exclass

>>> raise ValueError
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError
  • raise exclass()

>>> raise ValueError()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError
  • raise exclass, args

>>> raise ValueError, 'Something wrong happen about value'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Something wrong happen about value
>>> 
>>> raise ValueError, ('New Error', 'Something wrong happen about value')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: ('New Error', 'Something wrong happen about value')
  • raise exclass(args)

>>> raise ValueError('Something wrong happen about value')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Something wrong happen about value
  • raise exclass, instance

>>> newError = ValueError('Something wrong happen about value')
>>> raise ValueError, newError
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Something wrong happen about value
>>> 
>>> newError = ValueError('Something wrong happen about value')
>>> raise IOError, newError
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IOError: Something wrong happen about value
# 注意看异常类型和异常参数
  • raise instance

>>> newError = ValueError('Something wrong happen about value')
>>> raise newError
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Something wrong happen about value
>>> 
>>> raise newError.__class__, newError
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Something wrong happen about value
  • raise

>>> raise
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: exceptions must be old-style classes or derived from BaseException, not NoneType
# 即达不到所描述的效果,即便前面已经有异常出现,仍是会触发TypeError异常




7.断言


        断言经过assert语句实现,测试一个表达式,若是返回值是假,触发异常。触发异常时,能够像处理普通异常同样对它进行处理。

  • 语法

assert expression[, arguments]

        举例以下:

>>> assert 1 == 1
>>> assert 1 == 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError
>>>
>>> assert 1 == 0, 'One does not equal zero silly!'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError: One does not equal zero silly!




8.标准异常


        全部的标准异常都是内建的,因此能够直接在交互器或执行脚本文件时使用,关于Python当前的标准异常集,其实只要查看源代码就能够很清晰地知道有哪些标准异常了,这里就再也不列出来了。

        另外,有3个直接从BaseException派生的异常子类:

  • SystemExit

  • KeyboardInterrupt

  • Exception

        其它的全部内建异常都是Exception的子类。




9.建立异常


        其实建立异常,只须要继承一个异常,并根据本身的须要进行定制便可,但因为目前还使用不到,因此先略过,实际上能够经过书上的例子和异常类的源代码来加深对Python面向对象编程的理解,日后再作整理。




10.(如今)为何用异常


        确定是须要用异常的,由于须要达到这样的目的:运行环境必须足够强健,来处理应用级别的错误,并提供用户级别的错误信息。这样才能提供良好的用户体验。




11.到底为何要异常


        没有异常,将会致使不少问题。

        



12.异常和sys模块


        能够经过sys模块中的exc_info()函数来获取异常信息,举例以下:

>>> try:
...     float('abc123')
... except:
...     import sys
...     exc_tuple = sys.exc_info()
... 
>>> print exc_tuple
(<type 'exceptions.ValueError'>, ValueError('could not convert string to float: abc123',), <traceback object at 0x7f9ba0fa0f38>)

        能够看到,从sys.exc_info()中获得一个三元组,元素分别以下:

  • exc_type:异常类

  • exc_value:异常类的实例

  • exc_traceback:跟踪记录对象

        跟踪记录对象提供了发生异常的上下文,包含诸如代码的执行帧,异常发生时的行号等信息。




13.相关模块


        以下:

异常相关的标准库
模块 描述
exceptions 内建异常(不须要导入这个模块)
contextlib 为使用with语句的上下文对象工具
sys 主要是sys.exc_info()
相关文章
相关标签/搜索