EAFP:先斩后奏的设计思路,异常处理的用武之地,pythonic 的编程哲学
今天分享一个 pythonic 的设计思路,缩写是 EAFP,全称是 easier to ask forgiveness than permission,直译过来是「请求原谅比请求许可更容易」。字面上还是挺费解的,我们来直接看例子。
概念说明
我们这里有个字典,包含 name 和 age 两个 key,然后我要写一个函数,用来打印这两个 key,那么这个函数会这样写。
1
2
3
4
profile = {'name': 'Tom', 'age': 33}
def print_profile(profile):
print(f'This is {profile["name"]}, {profile["age"]} years old.')
但这样写有个小问题,当传入一个不包含 name 和 age 的 key 时,就会报错。
一个常见的改法是这样。
1
2
3
4
5
def print_profile(profile):
if 'name' in profile and 'age' in profile:
print(f'This is {profile["name"]}, {profile["age"]} years old.')
else:
print('Missing keys!')
这,就是一开始说的请求许可。在一开始先询问是否可以这样做,能做才继续做。
与此对应的是另一种写法,请求原谅。
1
2
3
4
5
def print_profile(profile):
try:
print(f'This is {profile["name"]}, {profile["age"]} years old.')
except KeyError as e:
print(f'Missing "{e}" key!')
因为请求原谅这个表达实在过于抽象了,我更愿意称为先斩后奏。
那么为什么先斩后奏要优于请求许可呢,有两个理由。
- 效率更高。在请求许可的例子中,要执行预想的语句前,需要先读取两次对象来进行确认。反观先斩后奏,在执行语句的过程中再发现问题,省掉了预先读取对象。
- 可读性更强。如果要在执行一段代码前,先检查所有可能出错的条件,那会需要大量代码用于检查,模糊了主逻辑,不便于阅读。
那么现在,你应该能够明白 EAFP 的意思了。我想再分享几个常见的使用场景。
常见场景
读取列表索引
比如我们要读取列表的第三个 item。
1
2
def get_third_item(lst):
print(lst[2])
为了防止报错,可以在前面加个 if。
1
2
3
def get_third_item(lst):
if len(lst) >= 3:
print(lst[2])
这样运行前先询问可不可以的,即请求许可。与之相对应的是使用 try except 进行先斩后奏。
1
2
3
4
5
def get_third_item(lst):
try:
lst[2]
except IndexError as e:
print(e)
读取文件前先检查是否存在
这是一个没有任何处理直接读取的写法。
1
2
3
def read_file(filepath):
with open(filepath) as f:
print(f.read())
为了防止文件不存在,我们可以加上个 if,这是请求允许的写法。
1
2
3
4
5
6
def read_file_if(filepath):
if os.path.exists(filepath):
with open(filepath) as f:
print(f.read())
else:
print('File not exists!')
而先斩后奏的写法则是这样。
1
2
3
4
5
6
7
8
def read_file_try(filepath):
try:
f = open(filepath)
except FileNotFoundError as e:
print(e)
else:
with f:
print(f.read())
对 try except 的补充
最后我想再补充一下 try except 这个语句。它们的完全体是
1
2
3
4
5
6
7
8
try:
...
except:
...
else:
...
finally:
...
Try 表示可能会出错的语句,except 是如果出错了要进入的语句,你也可以在 except 这里加上你希望的特定出错,else 表示没有出错要执行的语句,finally 表示无论有没有出错都要执行的语句。
最后
我们讲解了 EAFP 的意思,并说明了如何使用,同时介绍了几个常见的使用场景,最后简单补充了 try except 语句的语法,希望对你有帮助。
This post is licensed under CC BY 4.0 by the author.