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

一、错误和异常

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 —— 包

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容

  • 一、简介 Python最强大的结构之一就是它的异常处理能力,所有的标准异常都使用类来实现,都是基类Exceptio...
    随风化作雨阅读 3,009评论 0 1
  • Python异常处理 异常概念: 异常:就是不正常的情况,程序开发过程中错误和BUG都是补充正常的情况 异常发生的...
    youngkun阅读 910评论 0 4
  • 处在大三这样一个关键的时间点,身边的人都在为自己的未来做打算,有的人想去北上广工作,有的人要读研究生,有的人要为出...
    那小娜阅读 432评论 3 3
  • 思考:“我与你”是不带有任何目的性的相处,文章讲的是这种相处好比是“神性在其中”,这种关系如果放在母亲与孩子之间,...
    杨雪雪阅读 57评论 0 0
  • 灵魂没有内涵灌溉,轻飘飘的空壳身体往上飘啊飘,越来越脱离世界。 大三最后一个学期,才开始醒悟,加上身边老师的呼吁,...
    橙大仙阅读 284评论 0 2