Python基础手册26——错误 & 异常

作者: 常大鹏 | 来源:发表于2018-02-11 11:32 被阅读77次

    一、错误和异常

    1、错误

    从软件方面来讲,错误通常是语法或逻辑上的。语法错误会导致程序代码不能被解释器解释,这些错误必须在程序执行前纠正。当程序的语法正确后,剩下的就是逻辑错误了。逻辑错误可能是由于不完整或是不合法的代码逻辑所致,还可能是由于代码逻辑无法生成或执行。

    编译时会检查语法错误,编译完成后 Python 解释器会在程序运行时检测逻辑错误。当检测到一个错误,Python 解释器就引发一个异常,并显示异常的详细信息。程序员可以根据这些信息迅速定位问题并进行调试。

    2、异常

    异常处理通常分为两个阶段:首先是解释器在执行代码过程中发生错误并引起异常,异常会在错误检测到的地方引发,并将当前流打断,然后是检测异常并执行相关的处理程序。

    异常引发后,可以调用很多不同的操作。可以是忽略错误(记录错误但不采取任何措施),或是减轻问题的影响后设法继续执行程序。所有的这些操作都代表一种程序的继续,关键是程序员在错误发生时可以指示程序如何执行。

    类似 Python 这样支持引发和处理异常(这更重要)的语言,可以让开发人员可以在错误发生时更直接地控制它们。程序员不仅仅有了检测错误的能力,还可以在它们发生时采取更可靠的补救措施。由于有了运行时管理错误的能力,应用程序的健壮性有了很大的提高。

    常见的异常情况

    NameError 表示我们访问了一个没有初始化的变量。

    任何数值被零除都会导致一个 ZeroDivisionError 异常。

    SyntaxError 异常是语法错误,是唯一不是在运行时发生的异常。它代表 Python 代码中有一个不正确的语法结构,在它改正之前程序无法执行。这些错误一般都是在编译时发生,Python 解释器无法把你的脚本转化为 Python 字节代码,当然这也可能是你导入一个有缺陷的模块的时候。

    IndexError 异常在你尝试使用一个超出范围的值来索引序列时引发。

    映射对象,例如字典,是依靠关键字(keys)访问数据值的。如果使用错误的或是不存在的键请求字典就会引发一个 KeyError 异常。

    类似尝试打开一个不存在的磁盘文件一类的操作会引发一个 FileNotFoundError 异常。

    属性和方法被定义后,我们可以使用熟悉的点操作符访问它,但如果是没有定义的属性,将导致一个 AttributeError 异常。

    二、try 语句

    要给你的代码添加错误检测及异常处理,只要将它们封装在 try-except 语句当中。 try 子句的代码块,就是你打算监测的代码,检查有无异常发生。except 句子的代码块,则是你处理异常的代码。

    在程序运行时,解释器尝试执行 try 子句块里的所有代码,如果代码块完成后没有异常发生,就会忽略 except 子句继续执行。当 try 子句块中发生异常时,Python 解释器会依次检查 except 子句,直到找到与异常匹配的子句,并执行其中的代码块。

    当找到一个匹配的 except 子句时,异常就被赋给 except 子句中 as 关键字后面的目标,然后执行 excep 子句的 suite。所有 except 子句必须有一个可执行块。

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

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

    如果同一异常存在两个嵌套处理程序,并且内部处理程序的 try 子句中发生异常,则外部处理程序将不处理异常。如果 except 子句头部的一个表达式引发了异常, 那么就会中断原异常处理器的搜索, 而在外层代码和调用栈上搜索新的异常处理器(就好像是整个 try 语句发生了异常一样)。

    1、try-except 语句

    我们使用 try-except 语句进行异常的监控,并且对监测到的异常进行处理。最常见的 try-except 语句语法如下所示。它由 try 子句和 except 子句组成,也可以有一个可选的 as 语句来保存错误原因。

    try:
        try_suite             # 监控这里的异常
    except Exception[as reason]:
        except_suite       # 异常处理代码
    

    2、处理多个异常的 except 语句

    我们可以在一个 except 子句里指定并处理多个异常。except 语句在指定多个异常时要求异常被放在一个元组里:

    try:
        try_suite    
    except (Exception1, Exception2)[as reason]:
        suite_for_Exception1_and_Exception2
    

    3、带有多个 except 的 try 语句

    你可以把多个 except 语句连接在一起,处理一个 try 块中可能发生的多种异常,我们可以分别为每个异常类型分别创建对应处理程序,这样就可以更加细致的处理错误并得到更详细的错误信息。

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

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

    4、捕获所有异常

    如果查询异常类继承的树结构,我们会发现 Exception 类是在最顶层的,所以我们可以在 except 子句中通过指定 Exception 异常来捕获所有的异常:

    try:
        pass
    except Exception [as e]:
        # error occurred, log 'e', etc.
    

    另一个我们不太推荐的方法是使用 裸 except 子句来匹配所有的异常:

    try:
        pass
    except:
        # error occurred, etc.
    

    虽然这样的代码可以捕获大多异常,但它不是好的 Python 编程样式。它会捕获所有异常,你可能会忽略掉重要的错误,正常情况下这些错误应该让调用者知道并做一定处理。通常我们只是在多个 except 组合子句的最后的 except 子句中来捕捉 Exception 异常。

    Python 提供给程序员的 try-except 语句是为了更好地跟踪潜在的错误并在代码里准备好处理异常的逻辑。它的目的是减少程序出错的次数并在出错后仍能保证程序正常执行。

    5、异常参数

    异常也可以有参数,当异常被引发后参数是作为附加帮助信息传递给异常处理器的。标准内建异常提供至少一个参数,指示异常原因的一个字符串。

    异常的参数可以在处理器里忽略,但 Python 中可以在 except 关键字的后面使用过 as 关键字来保存这个值。我们已经在上边接触到相关内容:要想访问提供的异常原因,你必须保留一个变量来保存这个参数。把这个参数放在 as 语句的后面,并接在 except 语句要处理的异常后面。except 语句的这个语法可以被扩展为:

    # single exception
    except Exception[as reason]:
        suite_for_Exception_with_Argument
    
    
    # multiple exceptions
    except (Exception1, Exception2, ..., ExceptionN)[as reason]:
        suite_for_Exception1_to_ExceptionN_with_Argument
    

    reason 将会是一个包含来自导致异常的代码的诊断信息的类实例。异常参数自身会组成一个元组,并存储为类实例(异常类的实例)的属性。上边的第一种用法中,reason 将会是一个 Exception 类的实例。

    对于大多内建异常,也就是从 StandardError 派生的异常,这个元组只包含一个指示错误原因的字符串。一般说来,异常的名字已经是一个满意的线索了,但这个错误字符串会提供更多的信息。操作系统或其他环境类型的错误,例如 IOError ,元组中会把操作系统的错误编号放在错误字符串前。

    6、else 子句

    我们已经看过 else 语句段配合其他的 Python 语句,比如条件和循环。至于 try-except 语句,它的功能和你所见过的其他 else 语句没有太多的不同:在 try 语句范围中没有异常被检测到时,执行 else 子句。

    else 子句范围中的任何代码运行前,try 范围中的所有代码必须完全执行成功(也就是,结束前没有引发异常)。

    try:
        pass
    except Exception[as reason]:
        pass
    else:
        pass
    

    7、finally 子句

    finally 子句是无论异常是否发生、是否捕捉都会执行的一段代码。你可以将 finally 子句仅仅配合 try 子句一起使用,也可以和 try-except(else 也是可选的,但是必须和 except 子句配合使用)一起使用。

    try:
        pass
    except Exception[as reason]:
        pass
    else:
        pass
    finally:
        pass
    

    另一种使用 finally 的方式是 finally 单独和 try 连用。

     try:
        pass
    finally:
        pass
    

    当在 try 子句范围中产生一个异常时,会立即跳转到 finally 语句段。当 finally 中的所有代码都执行完毕后,会继续向上一层引发异常。

    三、触发异常

    到目前为止,我们所见到的异常都是由解释器引发的,由于执行期间的错误而引发。程序员在编写 API 时可能希望手动触发异常,为此 Python 提供了一种机制可以让程序员明确的触发异常,就是 raise 语句。

    1、raise 语句

    rasie 一般的用法是:
    raise [SomeException [, args [, traceback]]]

    第一个参数 SomeExcpetion 是触发异常的名字。如果有,它必须是一个异常的类或实例,如果有其他参数(args 或 traceback),则必须提供 SomeExcpetion。

    第二个符号为可选的 args(比如参数,值)来传给异常。这可以是一个单独的对象也可以是一个对象的元组。如果 args 原本就是元组,那么就将其传给异常去处理;如果 args 是一个单独的对象,生成只有一个元素的元组(就是单元素元组)。大多数情况下,使用单一的字符串用来指示错误的原因。如果传的是元组,通常的组成是一个错误字符串,一个错误编号,可能还有一个错误的地址,比如文件,等等。

    最后一项参数,traceback,同样是可选的(实际上很少用它),如果有的话,则是当异常触发时新生成的一个用于异常-正常化(exception—normally)的追踪(traceback)对象。当你想重新引发异常时,第三个参数很有用(可以用来区分先前和当前的位置)。如果没有这个参数,就填写 None。

    最常见的用法为 SomeException 是一个类。不需要其他的参数,但如果有的话,可以是一个单一对象参数,一个参数的元组,或一个异常类的实例。如果参数是一个实例,可以由给出的类及其派生类实例化(已存在异常类的子集)。若参数为实例,则不能有更多的其他参数。

    2、断言

    断言是一句必须等价于布尔真的判定;此外,发生异常也意味着表达式为假。断言可以简简单单的想象为raise-if-not 语句。测试一个表达式,如果返回值是假,触发异常。如果断言成功不采取任何措施(类似语句),否则触发 AssertionError (断言错误)的异常。

    assert expression[, arguments]

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

    下面是我们如何用 try-except 语句捕获 AssertionError 异常:

    《Python基础手册》系列:

    Python基础手册 1 —— Python语言介绍
    Python基础手册 2 —— Python 环境搭建(Linux)
    Python基础手册 3 —— Python解释器
    Python基础手册 4 —— 文本结构
    Python基础手册 5 —— 标识符和关键字
    Python基础手册 6 —— 操作符
    Python基础手册 7 —— 内建函数
    Python基础手册 8 —— Python对象
    Python基础手册 9 —— 数字类型
    Python基础手册10 —— 序列(字符串)
    Python基础手册11 —— 序列(元组&列表)
    Python基础手册12 —— 序列(类型操作)
    Python基础手册13 —— 映射(字典)
    Python基础手册14 —— 集合
    Python基础手册15 —— 解析
    Python基础手册16 —— 文件
    Python基础手册17 —— 简单语句
    Python基础手册18 —— 复合语句(流程控制语句)
    Python基础手册19 —— 迭代器
    Python基础手册20 —— 生成器
    Python基础手册21 —— 函数的定义
    Python基础手册22 —— 函数的参数
    Python基础手册23 —— 函数的调用
    Python基础手册24 —— 函数中变量的作用域
    Python基础手册25 —— 装饰器
    Python基础手册26 —— 错误 & 异常
    Python基础手册27 —— 模块
    Python基础手册28 —— 模块的高级概念
    Python基础手册29 —— 包

    相关文章

      网友评论

        本文标题:Python基础手册26——错误 & 异常

        本文链接:https://www.haomeiwen.com/subject/haektftx.html