在接头动态捕捉异常时让我大吃一惊的是,可以让我找到埋没的Bug和兴趣...

有问题的代码


下面的代码来自一个产物中看起来是好的抽象代码 - slightly(!) .这是挪用一些统计数据的函数,然后举办处理惩罚 . 首先是用socket毗连获取一个值,大概产生了socket错误.由于统计数据在系统中不是至关重要的,我们只是记一下日志错误并继承往下走.

(请留意,这篇文章我利用doctest测试的 - 这代表代码可以运行!)

>>> def get_stats():

...     pass

...

>>> def do_something_with_stats(stats):

...     pass

...

>>> try:

...     stats = get_stats()

... except socket.error:

...     logging.warning("Can't get statistics")

... else:

...     do_something_with_stats(stats)

查找


我们测试时并没有发明不当, 但实际上我们留意到静态阐明陈诉显示一个问题:

$ flake8 filename.py

filename.py:351:1: F821 undefined name 'socket'

filename.py:352:1: F821 undefined name 'logging'

显然是我们没测试,这个问题是代码中我们没有引用socket 和 logging 两个模块.使我感想诧异的是,这并没有预先抛出NameError错,我觉得它会查找这些异常语句中的一些名词,如它需要捕获这些异常,它需要知道些什么呢!

事实证明并非如此,异常语句的查找是延迟完成的,只是评估时抛出异常. 不可是名称延迟查找,也可以定制显示声明异常做为'参数(argument)'.

这大概是功德,坏事,可能是令人厌恶的.

功德(上段中提到的)

异常参数可以以任意形式数值通报. 这样就答允了异常的动态参数被捕捉.

>>> def do_something():

...    blob

...

>>> def attempt(action, ignore_spec):

...     try:

...         action()

...     except ignore_spec:

...         pass

...

>>> attempt(do_something, ignore_spec=(NameError, TypeError))

>>> attempt(do_something, ignore_spec=TypeError)

Traceback (most recent call last):

 ...

NameError: global name 'blob' is not defined

坏事(上段中提到的)

这种明明的漏洞就是异常参数中的错误凡是只有在异常触发之后才会被留意到,不外为时已晚.当用异常去捕捉不常见的事件时(譬喻:以写方法打开文件失败),除非做个一个特定的测试用例,不然只有当一个异常(可能任何异常)被触发的时候才会知道, 届时记录下来而且查察是否有匹配的异常, 而且抛出它本身的错误异常 - 这是一个NameError凡是所做的工作.

>>> def do_something():

...     return 1, 2

...

>>> try:

...     a, b = do_something()

... except ValuError:  # oops - someone can't type

...     print("Oops")

... else:

...     print("OK!")   # we are 'ok' until do_something returns a triple...

OK!

令人讨厌的(上段中提到的)

>>> try:

...    TypeError = ZeroDivisionError  # now why would we do this...?!

...    1 / 0

... except TypeError:

...    print("Caught!")

... else:

...    print("ok")

...

Caught!

不只仅是异常参数通过名称查找, - 其它的表达式也是这样事情的:

>>> try:

...     1 / 0

... except eval(''.join('Zero Division Error'.split())):

...     print("Caught!")

... else:

...     print("ok")

...

Caught!

异常参数不只仅只能在运行时确定,它甚至可以利用在生命周期内的异常的信息. 以下是一个较量费解的方法来捕获抛出的异常 - 但也只能如此了:

>>> import sys

>>> def current_exc_type():

...     return sys.exc_info()[0]

...

>>> try:

...     blob

... except current_exc_type():

...     print ("Got you!")

...

Got you!

很明明这才是我们真正要寻找的当我们写异常处理惩罚措施时, 我们应该首先想到的就是这种

(字节)代码

为了确认它是如安在异常处理惩罚事情中呈现的,我在一个异常的例子中运行 dis.dis(). (留意 这里的解析是在Python2.7 下 - 差异的字节码是Python 3.3下发生的,但这根基上是雷同的):

>>> import dis

>>> def x():

...     try:

...         pass

...     except Blobbity:

...         print("bad")

...     else:

...         print("good")

...

>>> dis.dis(x)  # doctest: +NORMALIZE_WHITESPACE

 2           0 SETUP_EXCEPT             4 (to 7)

<BLANKLINE>

 3           3 POP_BLOCK

             4 JUMP_FORWARD            22 (to 29)

<BLANKLINE>

 4     >>    7 DUP_TOP

             8 LOAD_GLOBAL              0 (Blobbity)

            11 COMPARE_OP              10 (exception match)

            14 POP_JUMP_IF_FALSE       28

            17 POP_TOP

            18 POP_TOP

            19 POP_TOP

<BLANKLINE>

 5          20 LOAD_CONST               1 ('bad')

            23 PRINT_ITEM

            24 PRINT_NEWLINE

            25 JUMP_FORWARD             6 (to 34)

       >>   28 END_FINALLY

<BLANKLINE>

 7     >>   29 LOAD_CONST               2 ('good')

            32 PRINT_ITEM

            33 PRINT_NEWLINE

       >>   34 LOAD_CONST               0 (None)

            37 RETURN_VALUE

Python教程

2017-11-02


在接头动态捕捉异常时让我大吃一惊的是,可以让我找到埋没的Bug和兴趣...有问题的代码下面的代码来自一个产物中看起来是好的抽象代码 - sl...