作为一个编程新手,长期以来,我一直对于 exception
和 if
充满了疑惑,不知道为什么要使用 exception
,而我是从 C 语言开始学习,对于 exception
本生了解就不深。最近在一些实战和思考中对于 exception
有了一些自己的感悟,简单的记录一下。
为什么要使用exception?
有一点很明确的,从我第一天学习编程的时候老师就提到过的:任何一门编程语言,只要含有顺序
、选择
和循环
三种结构就能在理论上写出任何程序。那为什么还要exception
呢?答案很简单,为了写代码时更爽,写出的代码更加优雅。
一个例子
假设我正在给我的朋友们写一个库,作用是将一个文件中的注释全部去掉,保存在新文件中。我们假设这个库的名字叫做del_comment.py
这个库里面,我引用了两个包。一个包叫做 open_file.py
作用是打开文件,并猜出它的编码和编程语言。另一个包的名字叫做 save_file.py
,作用是保存文本成一个文件。
我们来假设 open_file.py
是这样写的:
1 | def open_file(file_name): |
不要在意代码的对错。我们只看它的作用。首先判断文件是否存在,不存在返回文件不存在
。然后获取编码方式,猜不出来就返回未知编码
。这两个都OK的话,返回打开的文件和编码方式。 那么对于这两个返回,我们有什么结局方法吗?很不幸,这个问题,我们解决不了,这个只有调用了这段代码的使用者才能处理。所以,我们要把这些错误传递给他。所以我们的del_comment.py
可能就要这么写:
1 | def del_comment(file_name): |
上面可以通过直接判断是不是返回的 (opened_file, coding)
来简化代码,但是为了演示,就写成上面这个样子。从上面的代码,可以看出来一个问题了。对于 文件不存在
and 未知编码
这两个问题,我(库作者)是没有办法进处理,只能交给用户(我的朋友,库的使用者)来处理。呃呃呃~将这样自己无法解决的事情放在这里,是不是很不优雅。但是,貌似还是可以接受的。那我们开始加入save_file.py
。我们假设它是这个样子的。 1
2
3
4
5
6
7
8
9
10def save_file(file):
if not has_enough_space():
return '磁盘空间不足'
if not has_permission():
return '没有在此处创建文件的权限'
processing_and_save_file()
return "SUCCESSFUL"
功能也十分的简单。先判断空间够不够,如果不够的话,返回 磁盘空间不足
。然后判断有没有权限,没有就返回没有在此处创建文件的权限
。都可以,就存文件,同时返回 'SUCCESSFUL'。那我们的 del_comment.py
就要扩展了。 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21def del_comment(file_name):
a = open_file(file_name)
if a == '文件不存在':
return '文件不存在'
if a == '未知编码':
return '未知编码'
file = processing_del_comment(file);
b = save_file(file);
if b == '磁盘空间不足':
return '磁盘空间不足'
if b == '没有在此处创建文件的权限':
return '没有在此处创建文件的权限'
if b == "SUCCESSFUL"
return "SUCCESSFUL"
呃呃呃~又来了,一大堆在底层发现的、我没有办法解决的异常,经由我传递去了上层
。那么,有没有什么办法,让我可以不用管那些我没办法处理的问题,而只处理那些我可以解决的问题呢?恭喜,exception
,我终于知道该怎么用 exception
了。
如果所有的地方都改用exception
,我们就不用管那些我们一个一个返回那些我们处理不了的问题了。因为exception
是全局的,任何人都能够catch
的到,我能解决我就catch
,我不能解决,我就不catch
。就算我手痒,受不了,catch
了,我也什么都做不了。改后代码如下: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34def open_file(file_name):
if not file.is_exist():
thorw FileDoesNotExist('文件不存在')
if not file.is_known_coding():
thorw UnknownCoding('未知编码')
opened_file = file.open()
coding = file.getCoding()
# 返回打开的文件和编码方式.
return (opened_file, coding)
def save_file(file):
if not has_enough_space():
thorw NotEnoughFreeSpace('磁盘空间不足')
if not has_permission():
thorw PermissionDenied('没有在此处创建文件的权限')
processing_and_save_file()
return "SUCCESSFUL"
def del_comment(file_name):
a = open_file(file_name)
try:
a = open_file(file_name)
except FileNotExist:
a = open_file('./')
file = processing_del_comment(a[0]);
return save_file(file)
注意,修改后的代码,对于空间不足,没有权限,未知编码我都没有处理。我只处理了文件不存在这个问题(将文件名改为当前目录)。而其它的我解决不了的问题,我都没有理会。
抛出的异常就一定要 catch 吗?
上面所说的,我们只处理自己能够处理的异常。那么假如有一个异常,没有一个人在任何一个地方可以处理,那怎么办?答案是,就让它去吧,让程序死掉吧。因为这已经不能称作一个异常了,这是一个错误,这种错误是程序员没办法拯救的。一个例子,假如申请了一块比内存还大的空间。这时候不管你在怎么尝试着去拯救,都只是做无用功。让它安心地死去吧,大家都得到痛快。
一定要要使用 exception ?
很明显,答案是否。不用说并不是所有的语言都有 exception,有些语言(比如 C++)包含异常,却不推荐(Google cpp style)使用。C 和 C++ 用错误码来处理也是非常成功的 practice。但是有一点,可以确认的是,使用 exception 可以让你的代码更加的优雅。