Python基础:23异常

一:概述

        1:错误

        错误有语法和逻辑上的区别。语法错误指示软件的结构上有错误,导致不能被解释器解释或编译器无法编译。这些错误必须在程序执行前纠正。逻辑错误可能是由于不完整或是不合法的输入所致,还可能是逻辑上无法生成,计算,或是输出结果需要的过程无法执行。

        当Python 检测到一个错误时,解释器就会指出当前流已经无法继续执行下去。这时候就出现了异常。

 

        2:异常

        对异常的最好描述是: 它是因为程序出现了错误而在正常控制流以外采取的行为. 这个行为又分为两个阶段: 首先是错误引发异常, 然后是检测,并采取可能的措施。

        第一个阶段是在出现错误后发生的. 只要检测到错误并且意识到异常条件,解释器会引发一个异常.

        Python 也允许程序员自己引发异常. 无论是 Python 解释器还是程序员引发的, 异常就是错误发生的信号. 当前流将被打断, 之后就是处理这个错误并采取相应的操作.便是第二阶段。

 

二:Python中的异常举例

        访问变量需要由解释器进行搜索,如果请求的名字没有在任何名称空间里找到, 那么将会生成一个 NameError 异常.

>>> foo

Traceback (innermost last): File "<stdin>", line 1, in ?

NameError: name 'foo' is not defined

 

        ZeroDivisionError: 除数为零

>>> 1/0

Traceback (innermost last): File "<stdin>", line 1, in ?

ZeroDivisionError: integer division or modulo by zero

 

        SyntaxError: Python 解释器语法错误

>>> for

File "<string>", line 1

for

^

SyntaxError: invalid syntax

        SyntaxError 异常是唯一不是在运行时发生的异常. 它代表 Python 代码中有一个不正确的结构, 在它改正之前程序无法执行。

 

        IndexError:请求的索引超出序列范围

>>> aList = []

>>> aList[0]

Traceback (innermost last): File "<stdin>", line 1, in ?

IndexError: list index out of range

 

        KeyError:请求一个不存在的字典关键字

>>> aDict = {'host': 'earth', 'port': 80}

>>> print aDict['server'] Traceback (innermost last):

File "<stdin>", line 1, in ?

KeyError: server

 

        IOError: 输入/输出错误,比如尝试打开一个不存在的磁盘文件一类的操作会引发一个操作系统输入/输出(I/O)错误.

>>> f = open("blah")

Traceback (innermost last):

File "<stdin>", line 1, in ?

IOError: [Errno 2] No such file or directory: 'blah'

 

        AttributeError: 尝试访问未知的对象属性

>>> class myClass(object):

... pass

...

>>> myInst = myClass()

>>> myInst.foo

Traceback (innermost last): File "<stdin>", line 1, in ?

AttributeError: foo

 

三:检测和处理异常

        异常可以通过try 语句来检测. 任何在 try 语句块里的代码都会被监测有无异常发生。

        一个 try语句可以对应一个或多个 except 子句, 但只能对应一个finally 子句, 或是一个try-except-finally 复合语句。

try-finally 只允许检测异常并做一些必要的清除工作(无论发生错误与否), 没有任何异常处理设施。

        还可以添加一个可选的 else 子句,处理没有探测到异常的时执行的代码。

 

        1:try-except

        try-except 语句(以及其更复杂的形式),进行异常监控,并且提供了处理异常的机制。语法如下:

try:

        try_suite                       #监控此处的异常

except  Exception[, reason]:

        except_suite                #异常处理代码

 

        例子如下:

try:

        f = open('blah', 'r')

except IOError, e:

        print 'could not open file:', e

 

    结果是:

could not open file: [Errno 2] No such file or directory

        在打开一个不存在的文件时仍然发生了 IOError .加入了探测和处理异常的代码. 当引发 IOError异常时, 告诉解释器让它打印出一条诊断信息. 而后程序继续执行, 而不像以前的例子那样直接退出。

 

        在程序运行时,解释器尝试执行 try 块里的所有代码, 如果代码块完成后没有异常发生, 执行流就会忽略 except 语句继续执行. 而当 except 语句所指定的异常发生后, 保存错误的原因, 控制流立即跳转到对应的处理器,而try 子句的剩余语句将被忽略。

        在上边的例子中,只捕获 IOError 异常. 任何其他异常不会被我们指定的处理器捕获。

 

        try 语句块中异常发生点后的剩余语句永远不会执行,一旦一个异常被引发, 就必须决定控制流下一步到达的位置,解释器将搜索处理器, 一旦找到, 就开始执行处理器中的代码。

        如果没有找到合适的处理器, 那么异常就向上移交给调用者去处理, 这意味着堆栈框架立即回到之前的那个. 如果在上层调用者也没找到对应处理器, 该异常会继续被向上移交, 直到找到合适处理器. 如果到达最顶层仍然没有找到对应处理器, 那么就认为这个异常是未处理的, Python 解释器会显示出跟踪返回消息, 然后退出。

 

        2:带多个except的try子句

        可以把多个 except 语句连接在一起, 处理一个 try 块中可能发生的多种异常, 如下所示:

try:

        try_suite

except  Exception1[, reason1]:

        suite_for_exception_Exception1

except  Exception2[, reason2]:

        suite_for_exception_Exception2

......

        首先尝试执行 try 子句, 如果没有错误, 忽略所有的 except 从句继续执行. 如果发生异常, 解释器将在这一串处理器(except 子句)中查找匹配的异常.如果找到对应的处理器,执行流将跳转到这里。比如:

def  safe_float(obj):

        try:

                retval = float(obj)

        except  ValueError:

                retval = 'could notconvert non-number to float'

        except  TypeError:

                retval = 'objecttype cannot be converted to float'

        return  retval

        使用错误的参数调用这个函数, 我们得到下面的输出结果:

>>> safe_float('xyz')

'could not convert non-number to float'

>>> safe_float(())

'argument must be a string'

 

        3:处理多个异常的except语句

        还可以在一个except 子句里处理多个异常。将多个异常被放在一个元组里:

try:

        try_suite

except (Exc1[, Exc2[, ... ExcN]])[, reason]:

        suite_for_exceptions_Exc1_to_ExcN

 

    例子如下:

def  safe_float(obj):

        try:

                retval = float(obj)

        except  (ValueError,  TypeError):

                retval = 'argumentmust be a number or numeric string'

        return  retval

        现在, 错误的输入会返回相同的字符串:

 

        4:捕获所有异常

        自版本 1.5 后, 异常成为类,因而现在有一个异常继承结构可以遵循。根据异常继承的树结构,Exception 是在最顶层的, 所以可以捕获所有的异常:

try:

        ......

except  Exception, e:

        # error occurred, log 'e', etc.

 

        另一种不太推荐的方法是使用裸 except 子句:

try:

        ......

except:

        # error occurred, etc.

        这个语法不如前一个 "Pythonic" 。它不是好的 Python 编程样式。

 

        另外,有些异常不是由于错误条件引起的。它们是 SystemExit 和KeyboardInterupt。 SystemExit是由于当前Python 应用程序需要退出, KeyboardInterupt 代表用户按下了 CTRL-C , 想要关闭 Python。

        在 Python 2.5中,异常被迁移到了 new-styleclass 上。启用了一个新的"所有异常的基类",这个类叫做 BaseException , 异常的继承结构有了少许调整。KeyboardInterrupt和 SystemExit 被从Exception 里移出, 和Exception 平级:

- BaseException

 |- KeyboardInterrupt

 |- SystemExit

 |- Exception

 |- (all other current built-in exceptions) 所有当前内建异常

 

        因此,如果需要捕获所有异常,需要下面的语法:

try:

        ......

except  (KeyboardInterupt,  SystemExit):

        # user wants to quit

        raise

except  Exception:

        # handle real errors

 

    或者是:

try:

        ......

except  BaseException, e:

        # handle all errors

 

        注意,一个不正确的使用方法就是把一大段程序放入一个 try 块中, 再用一个通用的except 语句“过滤”掉任何致命的错误并忽略它们:

# this isreally bad code

try:

        large_block_of_code             

except  Exception:

        pass                       

        要避免像上面那样,把大片的代码装入 try-except 中然后使用 pass 忽略掉错误。

 

        5:异常参数

        异常也可以有参数, 异常引发后它会被传递给异常处理器。异常的参数可以在处理器里忽略, 但 Python 提供了保存这个值的语法:

# single  exception

except  Exception[, reason]:

        suite_for_Exception_with_Argument

 

# multiple  exceptions

except  (Exception1, Exception2,..., ExceptionN)[, reason]:

        suite_for_Exception1_to_ExceptionN_with_Argument

 

        reason是异常类的实例,该实例有个属性,名为args,args是个元组,其中包含了该异常的提示信息:

def  safe_float(obj):

       try:

              retval = float(obj)

       except  ValueError,e:

              print'type of e is ', type(e)

              print'e is ', e

              print'e.args is ', e.args

              retval = 'could notconvert non-number to float'

       except  TypeError,e:

              print'type of e is ', type(e)

              print'e is ', e

              print'e.args is ', e.args

              retval = 'object typecannot be converted to float'

       return  retval

>>> a = safe_float([])

type of e is <type 'exceptions.TypeError'>

e is  float()argument must be a string or a number

e.args is ('float() argument must be a string or a number',)

>>> a = safe_float('abc')

type of e is <type 'exceptions.ValueError'>

e is  could notconvert string to float: abc

e.args is  ('couldnot convert string to float: abc',)

 

        对于大多内建异常, args元组只包含一个指示错误原因的字符串。操作系统或其他环境类型的错误, 例如 IOError , 元组中会把操作系统的错误编号放在错误字符串前。

        无论 reason.args 只包含一个字符串或是由错误编号和字符串组成的元组, 调用 str(reason) 总会返回一个良好可读的错误原因。reason 是一个类实例,因此本质上还是调用类的特殊方法 __str__() .

 

        其实,上面的例子可以简化为下面的代码:

def  safe_float(obj):

       try:

              retval = float(obj)

       except  (ValueError, TypeError),e:

              retval = str(e)

       return  retval

 

>>> safe_float('xyz')

'could not convert string to float: xyz'

>>> safe_float({})

'float() argument must be a string or a number'

 

        6:else子句

        else除了可以配合条件和循环之外,还可以与try-except 语句段配合,它的功能和其他else 没有太多的不同:如果在try范围中的所有代码都完全成功,也就是在结束前没有引发异常。则执行在else 范围中的代码。

        例子如下:

log = open('logfile.txt', 'w')

try:

        function()

except:

        log.write("*** caughtexception in module\n")

else:

        log.write("*** no exceptionscaught\n")

log.close()

        根据运行时是否引发异常,在日志中写入不同的消息。

 

        7:finally 子句

        finally 子句是无论异常是否发生都会执行的一段代码。从Python 2.5 开始, 可以用finally 子句与try-except 或try-except-else 一起使用。下面是try-except-else-finally语法的示例:

try:

        A

except  MyException:

        B

else:

        C

finally:

        D

 

        try语句可以有不止一个的except 子句,但最少有一个except 语句,而else 和finally 都是可选的。上述代码可能的顺序是A-C-D[正常]或A-B-D[异常]。无论异常发生在A,B,和/或C,都将执行finally 块。

 

        另一种使用finally 的方式是finally 单独和try 连用。这个try-finally 语句和try-except区别在于它不是用来捕捉处理异常的,而是用来在无论异常是否发生,都要维持一致的行为的情况。因为无论try中是否有异常触发,finally 代码段都会被执行

try:

        try_suite

finally:

        finally_suite #无论如何都执行

       

        当在try 范围中产生一个异常时,则将该异常暂存,然后立即跳转到finally 语句段。当finally 中的所有代码都执行完毕后,如果有保存的异常,则会继续向上一层引发异常。但是如果在finally中,引发了另一个异常,或者调用了return或break语句,则会丢弃保存的异常。

        比如打开文件,读文件的操作:

ccfile = None

try:

        try:

                ccfile =open('carddata.txt', 'r')

                txns = ccfile.readlines()

        finally:

                if  ccfile:

                        ccfile.close()

except  IOError:

        log.write('no txns thismonth\n')

 

        注意,无论选择什么语法,都至少要有一个except 子句,而else 和finally 都是可选的。

 

四:触发异常

        可以通过raise语句,明确的触发异常。

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

 

        如果raise后没有参数,则raise重新抛出当前作用域中最后一个激活的异常。如果当前作用域中没有激活的异常,则抛出TypeError异常以表示这是一个错误(如果在IDLE中运行,则会抛出Queue.Empty异常)。

 

        raise还可以跟三个参数,前两个参数用于决定异常的类型和值。raise最常用的方式是其后跟一个类,不需要其他参数。

        如果第一个参数是一个类,那么它将成为异常的类型。第二个参数用于决定异常的值:如果它是类的实例,那么该实例将成为异常的值。如果第二个参数是一个元组,它用于类构造函数的参数列表;如果它是None,则使用一个空的参数列表。

        如果存在第三个参数且不为None,那么它必须是一个回溯对象,且它将替换当前异常发生的位置。如果存在第三个参数且值不是回溯对象或者None,将会抛出TypeError异常。

        如果第一个参数是一个实例,那么将该实例的类作为异常的类型,而实例本身则作为异常的值,第二个参数必须是None。也就是: raise instance等价于raise instance.__class__, instance

 

五:断言

        断言是一句必须等价于布尔真的判定:测试一个表达式,如果返回值是假,触发AssertionError(断言错误)的异常。如果断言成功则不采取任何措施。断言通过assert 语句实现,在1.5 版中引入。

 

        AssertionError 异常和其他的异常一样可以用try-except语句块捕捉,但是如果没有捕捉,它将终止程序运行而且提供一个如下的traceback:

>>> assert  1 == 0

Traceback (innermost last):

File "<stdin>", line 1, in ?

AssertionError

 

        可以提供一个异常参数给assert 命令:

>>> assert 1== 0, 'One does not equal zero silly!'

Traceback (innermost last):

File "<stdin>", line 1, in ?

AssertionError: One does not equal zero silly!

        下面是用try-except 语句捕获AssertionError 异常:

try:

        assert  1 == 0,  'One does not equal zero silly!'

except  AssertionError, args:

        print  '%s: %s' % (args.__class__.__name__, args)

 

        从命令行执行上面的代码会导致如下的输出:

AssertionError:  One does not equalzero silly!

 

六:sys模块

        另一种获取异常信息的途径是通过sys 模块中exc_info()函数.此功能提供了一个3 元组的信息。例子如下:

>>> try:

...          float('abc123')

... except:

...           import  sys

...           exc_tuple = sys.exc_info()

>>> print  exc_tuple

(<class exceptions.ValueError at f9838>,

<exceptions. ValueError instance at 122fa8>,

<traceback object at 10de18>)

 

>>> for eachItem in exc_tuple:

...   print eachItem

...  

exceptions.ValueError

invalid literal for float(): abc123

<traceback object at 10de18>

 

        从sys.exc_info()得到的元组中是有:

exc_type: 异常类

exc_value: 异常类的实例

exc_traceback: 追踪(traceback)对象

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值