您的位置:首页 > 编程语言 > Python开发

Python——异常编码细节

2016-02-25 15:04 537 查看
异常编码细节

这次将学习try、raise、assert和with语句背后的细节。正如我们所看到的,尽管这些语句大多比较简单,但它们提供了强大的工具来处理Python代码中的异常。

=================================================================================

try/except/else语句

try是复合语句,它的最完整的形式如下所示。首先是以try作为首行,后面紧跟着缩进的语句代码,然后是一个或多个except分句来识别要捕捉的异常,最后是一个可选的else分句。try、except和else这些关键字会缩进在相同的层次。

try:
<statements>
except <name1>:
<statements>
except <name2,name3>:
<statemests>
except <name4> as <data>:
<statements>
except:
<statements>
else:
<statements>
在这个语句中,try首行底下的代码块代表此语句的主要动作:试着执行的程序代码。Except子句定义try代码块内引发的异常的处理器,而else子句(如果编写了的话)则是提供没发生异常时要执行的处理器。except分句会捕捉try代码块执行时所发生的任何异常,而else子句只在try代码块执行时不发生异常才会执行。

=================================================================================
try语句分句

编写try语句时,有一些分句可以在try语句代码块后出现。下表列出了所有可能的形式:

分句形式
说明
except:
捕捉所有(其他)异常类型
except name:
只捕捉特定的异常
except name as value:
捕捉所列的异常和其额外的数据(或实例)
except (name1,name2):
捕捉任何列出的异常
except (name1,name2) as value:
捕捉任何列出的异常,并取得其额外数据
else:
如果没有引发异常,就运行
finally:
总是会运行此代码块,不管有没有发生异常
【1】.except子句没列出异常名称(except:)时,捕捉没在try语句内预先列出的所有异常。

【2】.except子句以括号列出一组异常[except (e1 , e2 , e3):]会捕捉所列出的任何异常。
Python会从头到尾检查except子句,在某个try中寻找是否有相符者,所以括号版本就像是每个异常列在其except子句内,但是语句主体只需要编写一次而已。以下是多个except子句的例子,示范处理器的具体化:

try:
action()
except NameError:
...
except IndexError:
...
except KeyError:
...
except (AttributeError,TypeError,SyntaxError):
...
else:
...
Python会从头到尾以及从左到右查看except子句,然后执行第一个相符的except下的语句,如果没有符合的,异常会向这个try外传递。

如果想要编写通用的“捕捉一切”分句,空的except就可以做到。

try:
action()
except NameError:
...
except IndexError:
...
except:
...
else:
...
空的except子句是一种通用功能:因为这是捕捉任何东西,可让处理器通用化或具体化。在有时候,这种形式更方便以下,例如,下面是捕捉一切,但没列出任何事件的例子。

try:
action()
except:
...
不过,空的except可能引发一些设计问题:尽管方便,也可能捕捉和程序代码无关、意料之外的系统异常,而且可能意外拦截其他处理器的异常,例如,在Python中,即便是系统离开调用,也会触发异常。Python3引入了一个替代方法来解决这些问题之一——捕获一个名为Exception的异常,几乎与一个空的except具有相同的效果,但是,忽略和系统退出相关的异常。

try:
action()
except Exception:
...
--------------------------------------------------------------------------------------------------------------------------------------
例子:利用try/finally编写终止行为

以下为更加实际的例子,示范了finally子句的典型角色。
class MyError(Exception):
pass

def stuff(file):
raise MyError()

file  = open('data','w')
try:
stuff(file)
finally:
file.close()
print('not reached')
这段代码中,带有finally分句的try中包装了一个文件处理函数的调用,以确保无论函数是否发生异常,该文件总是会关闭。这样以后的代码就可确定文件的输出缓存区的内容已经从内存转移至磁盘了。类似的代码结构可以保证服务器连接已经关闭了。

=====================================================================================
统一try/except/finally语句

在Python2.5发布以前的所有Python版本中,try语句都有两种形式,而且都是独立的两种语句:我们可以用finally子句来确保清理代码一定执行,或者编写except代码块来捕捉和恢复特定的异常,还能定义选用的else分句。也就是,finally子句无法与except和else混合。

不过,在Python2.5及其以后的版本中,这两个语句已经合并。现在可以在同一个try语句中混合finally、except以及else子句。也就是,现在可以编写下列形式的语句:
try:
main-action
except Exception1:
handler1
except Exception2:
handler2
...
else:
else-block
finally:
finally-block
当像这样组合的时候,try语句必须有一个except或一个finally,并且其部分的顺序必须如下所示:

try -> except -> else -> finally

其中,else和finally是可选的,可能会有0个或多个except,但是,如果出现一个else的话,必须至少有一个except。
--------------------------------------------------------------------------------------------------------------------------------------

合并try的例子

下面代码编写了四种常见的场景,通过print语句来说明其意义:

sep = '-'*32+'\n'
print(sep+'exception raised and caught')
try:
x = 'spam'[99]
except IndexError:
print('except run')
finally:
print('finally run')
print('after run')

print(sep + 'no exception raised')
try:
x = 'spam'[3]
except IndexError:
print('except run')
finally:
print('finally run')
print('after run')

print(sep +'no exception raised,with else')
try:
x = 'spam'[3]
except IndexError:
print('except run')
else:
print('else run')
finally:
print('finally run')
print('after run')

print(sep +'exception raised but not caught')
try:
x = 1/0
except IndexError:
print('except run')
finally:
print('finally run')
print('after run')
运行结果如下:

--------------------------------
exception raised and caught
except run
finally run
after run
--------------------------------
no exception raised
finally run
after run
--------------------------------
no exception raised,with else
else run
finally run
after run
--------------------------------
exception raised but not caught
finally run
Traceback (most recent call last):
File "F:/data/PythonTest/2_25.py", line 47, in <module>
x = 1/0
ZeroDivisionError: division by zero
=====================================================================================
raise语句

要显示的触发异常,可以使用raise语句。raise语句的组成是:raise关键字,后面跟着可选的要引发的类或者类的一个实例:

raise <instance>
raise <class>
在Python3中异常总是类的实例,因此,这里第一个raise的形式是最常见的,直接提供一个实例,要么是在raise之前创建的,要么是raise语句自带的。如果我们传递一个类,Python调用不带构造函数参数的类,以创建被引发的一个实例,这个格式等同于在类引用后面添加圆括号。

看一些示例。对于内置异常,如下两种形式是对等的,都会引发指定的异常类的一个实例,但是,第一种形式隐式地创建实例。
raise IndexError
raise IndexError()
我们也可以提前创建实例——因为raise语句接受任何类型的对象引用,如下两个示例也可以引发IndexError:
exc = IndexError()
raise exc

excs = [IndexError,TypeError]
raise excs[0]
如果一个try包含一个名为except name as X:子句,变量X将会分配给引发中所提供的实例:
try:
...
except IndexError as X:
...
as在try处理器中是可选的,但是包含它将使得处理器能够访问实例中的数据以及异常类中的方法。如下示例,定义用户定义的的异常,并使用as关键字:
>>>
try:
raise MyExc('Gavin')
except MyExc as X:
print(X.args)

('Gavin',)
=====================================================================================
assert语句

Python还包括了assert语句,它可视为条件式的raise语句。该语句的形式为:
assert <test>,<data>
执行起来就像如下代码:
if __debug__:
if not <test>:
raise AssertionError(<data>)
也就是如果test计算为假,Python就会引发异常.AssertionError是内置异常,而__debug__标志位是内置变量名,除非有使用-0标志,否则自动设为1(真值)。

assert语句通常用于验证开发期间程序状况的,用来收集用户定义的约束条件,而不是捕捉内在的程序设计错误。应为Python会自行收集程序的设计错误。如下:
def reciprocal(x):
assert x != 0
return 1/x
这类assert一般都是多余的:因为Python会遇见错误时自动引发异常,让Python替你把事情做好就行了。
=====================================================================================

with/as环境管理器

Python3引入了一种新的异常相关的语句:with及其可选的as字句。这个语句的设计是为了和环境管理器对象一起工作。with/as语句的设计师作为常见try/finally用法模式的替代方案。就像try/finally语句,with/as语句也是用于定义必须的终止或“清理行为”,无论处理步骤是否发生异常。

with语句的基本格式如下:

with expression [as variable]:
with-block
在这里的expression要返回一个对象,从而支持环境管理协议。如果选用的as字句存在时,此对象也可返回一个值,赋值给变量名variable.

有些内置的Python对象已经得到强化,支持了环境管理协议,因此可以用于with语句。例如,文件对象有环境管理器,可在with代码块后自动关闭文件,无论是否引发异常。

with open(r'C:\test\data') as myfile:
for line in myfile:
print(line)
...
在这里,对open的调用,会返回一个简单的文件对象,赋值给变量名myfile.我们可以用一般的文件工具来使用myfile.

在这个with语句执行后,环境管理机制保证由myfile所引用的文件对象会自动关闭,即使处理该文件时,for循环引发了异常也是如此。然而,我们不能很容易地知道这会何时发生。with语句的这种用法作为一种替代,允许我们确定在一个特定代码块执行完毕后会发生关闭。但我们也可以使用更通用而明确的try/finally语句来实现类似的功能,但是,这需要4行管理代码而不是1行:
myfile = open(r'C:\test\data')
try:
for line in myfile:
print(line)
...
finally:
myfile.close()
除了文件对象,多线程模块所定义的锁和条件变量同步对象也可以和with语句一起使用,因为它们支持环境管理协议。后边会介绍。

-----------------------------------------------------------------------------------------------------------------------------------

环境管理协议这里不做介绍
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: