John | 曲

Reflection in Transition

第 7 章 异常与断言

7 EXCEPTIONS AND ASSERTIONS

曲政 / 2018-04-21


“exception” 的含义在 Python 中有什么不同?

英语中:not norm, thus rare.

python 里:not rare, everywhere.

最常出现的 exception 的类型有哪四种?

7.1 Handing Exceptions

exception 有哪两种命运?

  • raise an error, program terminates
  • be handled, and program continues

为什么要处理 exception?

除了因为 bug 引发的 exception,更多的 exception 可以并应该被预计到,然后得到相应的处理。

比如 - 尝试打开不存在的文件。 - 用户输入数据不合理。

一个检验输入数据是否合理的函数?

def read_val (val_type, request_msg, error_msg):
    while True:
        val = input (request_msg + " ")
        try:
            return (val_type (val))
        except ValueError:
            print (val, error_msg)


def test_read_val ():
    print (read_val (int, "Enter an integer:", "is not an integer"))


test_read_val ()

解释两句:

  • 它有 “多型性”,polymorphic.
  • int 是 type,也是 function,是 first-class object。在 python 里,一切都是对象。所谓 first-class object 可以理解为,它能作为函数的 arguments 和 return value。

为什么要承担处理 exception 的麻烦?

  • 让程序不中止。
  • 明确区分函数返回值的意义。比如 int ('abc') 返回的是 abc 的编码值,还是 None?
  • 忘记了写 except,程序会死机等,这是一个明确的 bug 信号。

except 有几种写法?

  • except:
  • except ValueError:
  • except (ValueError, TypeError):

7.2 Exceptions as a Control Flow Mechanism

函数计算出错了,怎么让它告诉我们?

大多数编程语言是让函数返回一个特定的值,类似 Python 的 None,让程序判断是否得到 None,是否出错。

有更好的做法:让函数自己就把错误信息显示出来,在它内部检查是否符合 specification,如果不符合,就把不符合的部分反馈出来。

在函数内可以用 raise exceptionName (arguments) 语句,构建错误信息。在函数外,用 except ValueError as msg: print ("some coment", msg) 把信息显示出来。

比如:

def get_ratios (vect1, vect2):
    """Assumes : vect1 and vect2 are equal lenght lists of numbers.
       Returns: s list containing the meaningful values of vect1 [1]/vect2 [2]."""
    ratios = []
    for index in range (len (vect1)):
        try:
            ratios.append (vect1 [index]/vect2 [index])
        except ZeroDivisionError:
            ratios.append (float ('nan'))
        except:
            raise ValueError ('get_ratios called with bad arguments')
    return ratios
    

def test_get_ratios ():
    try:
        print (get_ratios ([1.0, 2.0, 7.0, 6.0], [1.0, 2.0, 0.0, 3.0]))
        print (get_ratios ([], []))
        print (get_ratios ([1.0, 2.0], [3.0]))
    except ValueError as msg:
        print (msg)

输出

$ python3 fe_7_2_get_ratios.py 
[1.0, 1.0, nan, 2.0]
[]
get_ratios called with bad arguments

###nan 怎么用?

可以用 float ("nan") 把它作为浮点数使用,包含它的 expression 值也是 nan。

ratios.append (float ('nan'))
[1.0, 1.0, nan, 2.0]

为什么还要用except 包含不符合 specificaion 的情况?

检验一下又不会增加多少计算成本,而防范一下,defensive programming and checking,是个好习惯。

except:
    raise ValueError ('get_ratios called with bad arguments')

7.3 Assertions

为什么要加 assertion?

另一种 defensive programming 的习惯。检验中间值,检验函数返回值。

assert Boolean expression, argument

以上,2018-04-15 记。