Python中的EAFP原则是什么?

187
在Python中,“使用EAFP原则”是什么意思?你能给出任何例子吗?
4个回答

279

词汇表中:

宁愿请求原谅,也不要事先获得许可。这种常见的 Python 代码风格假定存在有效的键或属性,并在此假设被证明错误时捕获异常。这种简洁而快速的风格特点是有很多 tryexcept 语句。该技术与许多其他语言(如 C)常用的 LBYL 风格形成对比。

尝试访问字典键的示例。

EAFP:

try:
    x = my_dict["key"]
except KeyError:
    # handle missing key

LBYL:

(注:该缩写代表Look Before You Leap,意为先看后跳)
if "key" in my_dict:
    x = my_dict["key"]
else:
    # handle missing key

使用LBYL版本需要在字典中搜索两次键,并且可能被认为稍微不够易读。


53
另一个优点是避免了竞态条件,例如,只需尝试打开文件,如果您成功了,则成功了。而不是在先检查是否能够获取,然后尝试获取,最终发现在检查和访问尝试之间的微小时间内,您不能再获取它。 - Jon Clements
34
Python还提供了一种避免以上两种情况的方法,如果处理程序只是在键不存在时为 x 赋一个默认值:当 'key' 不在 my_dict 中时,x = mydict.get('key') 将返回 None;你也可以使用 .get('key', <something>),如果字典中没有这个键,那么 x 将被赋予这个 something。dict.setdefault()collections.defaultdict 也是避免增加额外代码的好工具。 - JAB
2
我认为except KeyErrorAttributeError是简单但最糟糕的例子之一。很多时候,由于将except AttributeError放在错误的位置,导致捕获了更深层次链中抛出的错误属性。我认为更好的例子是:try: open() ... except: IOError。或者try: parseLine() ... except ParseError - Ski
5
那是一个稍微不同的问题。您应该尽可能保持try块最简,以避免捕获错误的异常。同时请注意,我通常不偏好EAFP风格。我只是在回答问题,并陈述有些人喜欢它。我会根据情况逐个决定哪种代码最易读。 - Sven Marnach
4
值得一提的是,Grace Hopper 可能是这个短语的来源,她曾说过:“敢为人先。请求原谅总比征得许可容易。”(并不仅限于编程领域)。 - Fabien Snauwaert
我想补充一点,捕获过于宽泛的异常被认为是不好的实践。虽然包含 except Exception 可以捕获任何可能发生的问题,但最好还是明确地捕获错误。(请注意,此示例包括显式异常语句。) - DaveL17

29
我会用另一个例子来解释它。
在这里,我们试图访问文件并将其内容打印到控制台上。
LBYL - 先行判断再进行跳转:
我们可能希望检查是否可以访问该文件,如果可以,我们将打开它并将内容打印出来。如果我们无法访问该文件,我们将进入else部分。 这是一种竞争条件的原因是因为我们首先进行了访问检查。 当我们到达with open(my_file) as f:时,也许由于某些权限问题(例如,另一个进程获得了独占文件锁),我们无法再访问它了。 这段代码很可能会抛出错误,并且我们将无法捕获该错误,因为我们认为我们可以访问该文件。
import os

my_file = "/path/to/my/file.txt"

# Race condition
if os.access(my_file, os.R_OK):
    with open(my_file) as f:
        print(f.read())
else:
    print("File can't be accessed")

EAFP - 宁愿请求宽恕,而不是寻求许可:

在这个例子中,我们只是试图打开文件,如果无法打开,则会抛出IOError。如果可以,我们将打开文件并打印内容。因此,我们不是在请求做某事,而是在尝试做某事。如果成功了,太好了!如果失败了,我们就捕获错误并处理它。

# # No race condition
try:
    f = open(my_file)
except IOError as e:
    print("File can't be accessed")
else:
    with f:
        print(f.read())

我不确定将其描述为竞态条件是否正确。文件要么是可访问的,要么不可访问。 - ds4940
5
如果在第6行和第7行之间(即检查文件是否可访问和打开文件之间),文件的可访问性发生更改,那么就会出现竞态条件。 - Markus von Broady
@MarkusvonBroady同意了,编辑了答案并提供了竞争条件中另一个参与者的示例。 - ds4940
我一直认为遵循“宁愿检查再运行”(LBYL)的方式是做事情的首选,而不是使用“尝试,捕获异常”(try, except)块,这正确吗? - SurpriseDog
1
@SurpriseDog,也许在其他编程语言中不是这样,但在Python中是这样的。Python希望您使用异常处理,因此它已经被优化为在未抛出异常时高效运行。异常处理提高了可读性,因为错误处理代码在工作代码之后分组在一起,并且当需要处理每个可能的错误时,减少了缩进量。 - Mark Ransom
@SurpriseDog P.S. 这个答案中给出的异常示例并不是最佳实践,因为 else 块是完全不必要的 - 那段代码应该是 try 块的一部分。 - Mark Ransom

10
我把它称为“乐观编程”。意思是,大多数时候人们会做正确的事情,错误应该很少发生。因此,首先为“正确的事情”编写代码,然后如果出错,再捕获错误。 我的感觉是,如果用户要犯错误,他们应该承受时间的后果。使用工具正确方式的人可以快速完成任务。

2
使用“try-catch”而不是“if-else”。 - Alexandre Huat
我本意是寻找可能已知的错误,比如在电话号码字段中输入邮政编码。错误应该得到适当处理,而不是通过捕获例程来处理。 - Engineer

0
为了将 @sven-marnach 添加为回答者, 你可以使用:
dict[“key”] = dict.get(“key”, None)

这比他提到的为了做LBYL而进行两次搜索的问题要好。


这并没有回答问题。一旦你拥有足够的声望,你就可以评论任何帖子;相反,提供不需要提问者澄清的答案。- 来自审查 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接