什么是异常,其实我不是很清楚

起因

平时能较好的处理异常。但在自定义异常时才发现,并不了解异常。对于用的api也不真明白为什么要抛出异常,而不用其它方式。
 
针对自己的疑惑,觉得有必要好好思考下
 

异常机制的产生

语言发展是从汇编到C再到C++. 在汇编和C语言中都没有异常机制。
它们用的是错误编码机制. 直到C++才出现异常机制。
 
什么导致C++中出现异常机制?要回答它,需先了解错误编码机制的存在的缺点。
* 错误信息不全
调用信息不全; 错误种类很多,返回会过于简单.
* 默认可以被忽略
默认可以不用检查返回的错误编码,很容易使程序处在不正常状态.
* 不能跨作用域传送
只能一层层的向上传送,每层都有一堆检查错误的代码,会掩盖正常处理。
 
编码机制是为解决程序错误这个需求而生。在错误发生时最重要的是:
错误内容是什么?
在哪儿出错了?
为什么出错?
编码机制存在着缺点,不能很好解决这几个需求。
 
异常机制能完善的解决了这些问题. 所以后来语言中都加入了异常机制。
 
异常机制也就了两个重要特点:
* 沿着调用堆栈向上传递错误
* 不可忽略
前者可获得完整的调用信息,方便定位错误地点. 也可将错误处理代码分离出来。
后者明确拒绝不健康运行状态,是程序正确性的关键。
 

对异常的疑惑

一个简单的python异常
>>> a = {"a": 1, "b": 2}
>>> a.pop(c)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'c' is not defined
对于它我有很多疑问:
1. 为什么抛异常?返回特殊标记不行吗.
2. 什么是异常?为什么它会被定义为异常.
 
回答第一个问题
异常的特点是就是"不可忽略",上面的pop没有任何值适合作为标记,它只能抛异常. 如果忽略了,可能会让程序处在不健康状态。
 
回答第二个问题
异常是判断词,与它对的应是正常。
那么什么是正常?回答它也就可回答第二个问题了.
异常与正常其实隐藏着一个判断, 隐藏着一个判断标准。
 
那么谁在判断,谁在制定标准。答案是你。
你在决定什么是异常,什么时候该抛异常,抛什么类型的异常。
例子中就是python作者在背后做了思考/定义,决定它应该作为异常。
 

如何使用异常

当定义好什么是正常,知道哪些是不正常而且不可忽略时,就是使用异常的时候。为防止不健康运行状态. 而且不可忽略.
 
值得注意的是:通常是没法考虑全面的,会有漏掉的情况,应以保守的方法处理,对未考虑到的情况全部作为异常处理,再分辨,逐步完善。
 
异常的重要性
它是软件好坏的重要标志,任何在这上面处理不好的软件,无不是充满了坑。
 

错误默认值造成的郁闷

使用错误的默认值,会掩盖错误,又造成新的错误,最后将错误传入内部。

# 前台传入三个参数,三个参数都是必需的.
identifier = web.input().get("identifier", None)   # webpy框架
year = web.input().get("year", None)
taxpayer_name = web.input().get("taxpayer_name", None)
# 传输如None参数,会导致接口出现错误
result = service.inquiryTaxOwe(identifier, year, taxpayer_name)

前台传入三个参数时,表现正常。

上面使用默认参数是demo阶段时为了方面。实现时会出现下面的情况.

如果人来用它,只传入部分参数,结果发现程序有异常(inquirytaxOwe函数内抛出的)。它会找到实现接口的人,告诉你你的程序有问题。这个时候你怎么解释? 异常是里面一层抛出的,这一层可不管调用者的事,调用者只负责传参数,然后拿到结果。这时你会发现多么的郁闷。说不是你的责任,但是有你一分。说不是调用者的责任,实际也有它一分。
 
换一种方式
identifier = web.input().identifier
year = web.input().year
taxpayer_name = web.input().taxpayer_name
result = service.inquirytaxOwe(identifier, year, taxpayer_name)

再不传入参数时,会及早发现,不会再有上面的郁闷了.

令人不爽的数据检查

每当看到,用if检查数据,正确则把参数参入xxx函数执行,总是很不爽.

不仅感觉难看,而且使逻辑混乱.

数据合法性判断是函数自身的职责,如果将这个职责移到外面,它就是一个污染.

使用这个函数的地方越多污染的也越多.