理解Python语言里的异常(Exception)
Exception is as a sort of structured "super go to".
异常是一种结构化的"超级goto".
作为一个数十年如一日地钟爱C语言的程序员(因为C程序员需要记忆的关键字很少,而且可以很惬意地玩内存),对于高级语言如Python里的异常(Exception)一直不甚理解,尤其是其实现机理。但读了《Learning Python》一书中上面这句话(尤其是goto关键字)后,忽然豁然开朗。
如果用C语言编写一个鲁棒性良好的程序,一般得在每一个可能出错的运算之后检查返回值或状态码,然后在程序执行的时候根据返回值或状态做出不同的处理。例如:
int doStuff()
{ /* C program */
if (doFirstThing() == ERROR) /* Detect errors everywhere */
return ERROR; /* even if not handled here */
if (doNextThing() == ERROR)
return ERROR;
...
return doLastThing();
} int main()
{
if (doStuff() == ERROR)
badEnding();
else
goodEnding();
...
}
实际上,在现实的C程序中,通常用于处理错误检查和用于实际工作的代码数量相当。 但是, 在Python中,程序员就不用那么小心翼翼和神经质了。你可以把程序的任意片段包装在异常处理器内,然后编写从事实际工作的部分,假设一切都工作正常。 例如:
def doStuff(): # Python code
doFirstThing() # We don't care about exceptions here,
doNextThing() # so we don't need to detect them
...
doLastThing() if __name__ == '__main__':
try:
doStuff() # This is where we care about results,
except: # so it's the only place we must check
badEnding()
else:
goodEnding()
...
在Python代码中,完全没有必要让所有代码都去预防错误的发生,因为一旦有异常发生,运行Python代码的控制权就会立即跳转到相应的异常处理程序。再者,因为Python解释器会自动检查错误,所以Python代码通常不需要事先检查错误。归根结底一句话,异常(Exception)让程序员大致上可以忽略罕见的情况,并避免编写那些(烦人的但又不得不写的)错误检查代码。 //英文原文如下:
Because control jumps immediately to a handler when an exception occurs, there's
no need to instrument all your code to guard for errors. Moreover, because
Python detects errors automatically, your code usually doesn’t need to check for
errors in the first place. The upshot is that exceptions let you largely ignore
the unusual cases and avoid error-checking code.
1. Why Use Exceptions? 为什么使用异常
In a nutshell, exceptions let us jump out of arbitrarily large chunks of a program.
简而言之,异常让我们从一个程序中任意大的代码块中跳将出来。

2. Exception Roles 异常充当的最常见的几种角色
- Error handling 错误处理
- Event notification 事件通知
- Special-case handling 特殊情况处理
- Termination actions 行为终止
- Unusual control flows 非常规控制流


3. Exceptions: The Short Story 异常处理简明教程
3.1 默认异常处理器 (Default Exception Handler)
当我们的代码没有刻意去捕获某个异常的时候,一旦有致命错误发生,解释器将启动默认的异常处理器,例如:
$ python
Python 2.7.6 (default, Jun 22 2015, 18:00:18)
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> def fetcher(obj, index):
... return obj[index]
...
>>> x = 'spam'
>>> fetcher(x, 3)
'm'
>>>
>>> fetcher(x, 4)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in fetcher
IndexError: string index out of range
>>>
3.2 捕获异常 (Catching Exceptions)
很多时候,我们并不希望执行默认的异常行为,而是即便异常发生之后,我们的代码还能继续运行下去。这样,我们可以自己捕获异常。例如:
$ python
Python 2.7.6 (default, Jun 22 2015, 18:00:18)
...<snip>...
>>> def fetcher(obj, index):
... return obj[index]
...
>>> def catcher(obj, index):
... try:
... fetcher(obj, index)
... except IndexError:
... print "got exception"
... print "continuing"
...
>>>
>>> x = 'spam'
>>>
>>> catcher(x, 3)
continuing
>>>
>>> catcher(x, 4)
got exception
continuing
>>>
这里,我们使用了try ... except ...捕获异常。
3.3 引发异常 (Raising Exceptions)
异常能有Python解释器引发,当然也能由我们自己写的Python程序引发。
3.3.1 无条件引发异常 (raise)
$ python
Python 2.7.6 (default, Jun 22 2015, 18:00:18)
...<snip>...
>>> raise IndexError
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError
>>>
>>>
>>> try:
... raise IndexError
... except IndexError:
... print "got exception"
...
got exception
>>>
如果没捕捉异常,用户定义的异常就会向上传递,直到顶层默认的异常处理器,并通过标准出错信息终止该程序。
3.3.2 有条件引发异常 (assert)
assert也可以用来引发异常,它是一个有条件的raise,主要在开发过程中用于调试。例如:
$ python
Python 2.7.6 (default, Jun 22 2015, 18:00:18)
...<snip>...
>>>
>>> assert False, "Nobody expects the Spanish Inquisition!"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError: Nobody expects the Spanish Inquisition!
>>>
>>> assert True, "Nobody expects the Spanish Inquisition!"
>>>
3.4 用户定义的异常 (User-Defined Exceptions)
在3.3.1中,使用raise语句引发的异常是Python的内置作用域中定义的一个内置异常。当然,我们也可以定义自己的异常。用户定义的异常能够通过类编写,它继承一个内置的异常类:通常这个类的名称叫做Exception。基于类的异常允许脚本建立异常类型、继承行为以及附加状态信息。例如:
$ python
Python 2.7.6 (default, Jun 22 2015, 18:00:18)
...<snip>...
>>>
>>> class Bad(Exception): # user-defined exception
... pass
...
>>> def doomed():
... raise Bad() # raise an instance
...
>>> try:
... doomed()
... except Bad: # catch class name
... print "got Bad"
...
got Bad
>>>
3.5 终止行为 (Termination Actions)
Finally, try statements can say "finally" -- that is, they may include finally blocks. These look like except handlers for exceptions, but the try/finally combination specifies termination actions that always execute "on the way out," regardless of whether an exception occurs in the try block.
最后,try语句可以说"finally"。也就是说,它可以包含finally代码块。这看上去就像异常的except处理器,但是try/finally组合,可以定义一定会在最后执行时的收尾行为,无论try代码块是否发生了异常。 例如:
$ python
Python 2.7.6 (default, Jun 22 2015, 18:00:18)
...<snip>...
>>>
>>> def fetcher(obj, index):
... return obj[index]
...
>>> x = 'spam'
>>> try:
... fetcher(x, 3)
... finally: # Termination actions
... print "after fetch"
...
'm'
after fetch
>>>
>>> try:
... fetcher(x, 4)
... finally: # Termination actions
... print "after fetch"
...
after fetch
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "<stdin>", line 2, in fetcher
IndexError: string index out of range
>>>
由此可见,无论有没有异常发生,都会执行finally子句。 当然,在实际应用中,我们通常使用try ... expect .... finally组合。 try/except组合用于捕获异常并从中恢复,而try/finally组合确保无论try代码块内的代码是否发生了异常,终止行为一定会运行。(典型的应用是,没有异常按照正常流程走,有异常的时候则执行Error-handing操作;任何情况下最后都做cleanup操作)。例如:
veli$ ls -l /tmp/foo.txt
-rw-r--r-- 1 root root 12 Jun 4 18:19 /tmp/foo.txt
veli$ ls -l /tmp/bar.txt
-rw-r--r-- 1 veli veli 0 Jun 4 21:33 /tmp/bar.txt veli$ python
Python 2.7.6 (default, Jun 22 2015, 18:00:18)
...<snip>...
>>> def writer(file, s):
... fd = None
... try:
... fd = open(file, "w")
... fd.write("%s\n" % s)
... except:
... print "ERROR: fail to open file %s" % file
... finally:
... print "close file"
... if fd is not None:
... fd.close()
...
>>> s="hello world again"
>>> writer("/tmp/foo.txt", s)
ERROR: fail to open file /tmp/foo.txt
close file
>>>
>>> writer("/tmp/bar.txt", s)
close file
>>> veli$ ls -l /tmp/foo.txt
-rw-r--r-- 1 root root 12 Jun 4 18:19 /tmp/foo.txt veli$ ls -l /tmp/bar.txt && cat /tmp/bar.txt
-rw-r--r-- 1 veli veli 18 Jun 4 21:34 /tmp/bar.txt
hello world again
上面的writer()使用的是try ... except ... finally,可以用with ... as ...代替,例如:
veli$ python
Python 2.7.6 (default, Jun 22 2015, 18:00:18)
...<snip>...
>>> def writer(file, s):
... with open(file, "w") as fd:
... fd.write("%s\n" % s)
...
>>>
>>> s = "hello world again"
>>>
>>> writer("/tmp/foo.txt", s)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in writer
IOError: [Errno 13] Permission denied: '/tmp/foo.txt'
>>>
>>> writer("/tmp/bar.txt", s)
>>>
3.6 小结 (Summary)
| Statement | Meaning |
| try/except | Catch and recover from exceptions raised by Python, or by you. |
| try/finally | Perform cleanup actions, whether exceptions occur or not. |
| raise | Trigger an exception manually in your code. |
| assert | Conditionally trigger an exception in your code. |
| with/as | Implement context managers in Python 2.6+. |
参考资料:
1. Book: Learning Python, Fourth Edition : PART VII Exceptions and Tools
理解Python语言里的异常(Exception)的更多相关文章
- Java 里的异常(Exception)详解
作为一位初学者, 本屌也没有能力对异常谈得很深入. 只不过Java里关于Exception的东西实在是很多. 所以这篇文章很长就是了.. 一, 什么是java里的异常 由于java是c\c++ ...
- 010 深入理解Python语言
目录 一.概述 二.计算机技术的演进 2.1 计算机技术的演进过程 三.编程语言的多样初心 3.1 编程语言有哪些? 3.2 不同编程语言的初心和适用对象 3.3 2018年以后的计算环境- 四.Py ...
- 深入理解python语言
2008年,安卓操作系统诞生:PC时代向移动时代转换 互联网,视窗 2017/5/27柯洁最终0:3AlphaGo 计算机技术的演进过程 不同编程语言的设计初心和适用对象 C语言核心解决的是性能问题, ...
- 第三章 深入理解python语言
计算机技术的演进过程 1946-1981年 计算机系统结构时代(35年) 解决计算机能力的问题 1981-2008年 网络和视窗时代(27年) 解决交互问题 2008-2016年 复杂信息系统时代(8 ...
- 理解 Python 语言中的 defaultdict
众所周知,在Python中如果访问字典中不存在的键,会引发KeyError异常(JavaScript中如果对象中不存在某个属性,则返回undefined).但是有时候,字典中的每个键都存在默认值是非常 ...
- Python logging 模块打印异常 exception
logger.exception(sys.exc_info())
- Python语言学习之Python入门到进阶
人们常说Python语言简单,编写简单程序时好像也确实如此.但实际上Python绝不简单,它也是一种很复杂的语言,其功能特征非常丰富,能支持多种编程风格,在几乎所有方面都能深度定制.要想用好Pytho ...
- 【学习笔记】PYTHON语言程序设计(北理工 嵩天)
1 Python基本语法元素 1.1 程序设计基本方法 计算机发展历史上最重要的预测法则 摩尔定律:单位面积集成电路上可容纳晶体管数量约2年翻倍 cpu/gpu.内存.硬盘.电子产品价格等都遵 ...
- 《Python语言程序设计》【第1周】Python基本语法元素
实例:温度转化 #TempConvert.py 单行注释 ''' TemConvert.py ''' # 多行注释 TempStr = input("请输入带有符号的温度值: ") ...
随机推荐
- 基于Extjs的web表单设计器 第一节
前面一节介绍了表单设计器的背景和最终的大概样式,本节主要介绍表单设计器的需求及功能设计. 在讲需求之前先明确几个常用的概念: 主表或者卡片表——具有多行多列的一个区域的控件块,如下图所示. 明细表—— ...
- Buffer Pool--内存总结2
按内存划分: 1.DATABASE CACHE 用于存放数据页面的缓冲区,8KB每页 2.各项组件 A)数据库连接(CONNECTION) B)通用数据,如果事务上下文,表和索引的元数据 C)执行计划 ...
- .net 开发者尝试Apache Spark™
本文编译自一篇msdn magazine的文章,原文标题和链接为: Test Run - Introduction to Spark for .NET Developers https://msdn. ...
- Jenkins HA高可用参考
商用版Jenkins(CloudBee)提供HA插件,开源版本可以借助etcd服务发现+心跳脚本+sync的方式实现高可用.
- django 的 ajax 方式上传图片
页面代码: <html> <!-- 引入相关的js文件,相对路径 --> <script type="text/javascript" ...
- 论 BUG调试与(程序猿)初学者
作为一枚程序猿,BUG调试是最基本的技能,对于初学者更是重中之重.个人而言,要想为自己的程序猿生涯更上一层楼,就得知道什么是BUG调试,而且还必须知道怎么调好BUG.那么BUG究竟是什么呢?在我之前的 ...
- Wpf Page间跳转传参数 And Window To Page
这段时间用到Wpf,页面间的跳转网上有不少的示例,但是有些已经不能用了,尤其是页面间的传参问题更是一大堆,但正确的解决方案却没有几个,或者说写的不清楚,让人走了很多弯路,查看官方文档后发现了正确的姿势 ...
- 解决WebService中System.InvalidOperationException:缺少参数的问题
此问题在.Net 4.0 IIS7 Windows Server 2008下可能会出现. 现象是第一次正常调用,第二次接口报错. 删除CacheDuration即可.
- Spring Boot 学习系列(06)—采用log4j2记录日志
此文已由作者易国强授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 为什么选择log4j2 log4j2相比于log4j1.x和logback来说,具有更快的执行速度.同时也支 ...
- 瞄一眼,带你走进SparkSQL的世界
本文由 网易云发布. 作者:范欣欣(本篇文章仅限知乎内部分享,如需转载,请取得作者同意授权.) 最近想来,大数据相关技术与传统型数据库技术很多都是相互融合.互相借鉴的.传统型数据库强势在于其久经考验 ...