为什么#34;比获得许可更容易请求宽恕" (EAFP)认为Python的良好做法?作为编程新手,我的印象是,与使用其他检查相比,使用许多try...except
例程会导致代码膨胀且可读性较差。
EAFP方法的优势是什么?
注意:我知道这里有类似的问题,但它们主要是指一些具体的例子,而我对这一原则背后的哲学更感兴趣。
答案 0 :(得分:4)
LBYL,EAFP的计数器方法与断言没有任何关系,它只是意味着在您尝试访问可能不存在的内容之前添加一个检查。
Python是EAFP的原因与其他语言(例如Java)不同 - 在Python中捕获异常是相对便宜的操作,这就是为什么鼓励你使用它。
EAFP示例:
try:
snake = zoo['snake']
except KeyError as e:
print "There's no snake in the zoo"
snake = None
LBYL的例子:
if 'snake' in zoo:
snake = zoo['snake']
else:
snake = None
答案 1 :(得分:3)
你在这里混合两件事:断言和基于EAFP的逻辑。
断言用于验证函数的约定,即它的前后条件,有时还有它的不变量。它们确保以应该使用的方式使用函数。它们不适用于代码流,因为它们完全中断了错误执行。一个常见的例子是检查函数调用中的echo Mage::helper("adminhtml")->getUrl("adminhtml/index/index",
['_query' => ['test' => $testId]]);
参数。
在Python中,通常避免使用过多的断言。通常,您应该期望代码的用户正确使用它。例如,如果您记录的函数采用的参数不是None
,那么就没有必要使用一个验证该参数的断言。相反,只是期望有一个价值。如果由于None值而出现错误,那么无论如何它都会冒泡,因此用户知道他们做错了什么。但你不应该一直检查所有内容。
现在,EAFP有所不同。它用于控制流程,或者更确切地说,它避免了额外的控制流程,有利于期望事情正确并且如果不是则捕获异常。显示差异的一个常见示例是字典中的密钥访问:
None
现在看起来非常相似,但你应该记住LBYL解决方案检查字典两次。与捕获异常的所有代码一样,只有在不存在密钥的情况下才能执行此操作例外情况。因此,如果通常情况下,提供的密钥不在字典中,那么它是EAFP,您应该直接访问它。如果你不希望密钥出现在字典中,那么你应该首先检查它的存在(虽然异常在Python中更便宜,但它们仍然不是免费的,所以请保留它们以用于例外情况)。
EAFP的好处还在于你的库或应用程序的逻辑更深层次,# LBYL
if key in dic:
print(dic[key])
else:
handleError()
# EAFP
try:
print(dic[key])
except KeyError:
handleError()
来自上面,你可以假设这里传递了一个有效的密钥。所以你不需要在这里捕获异常,只是让它们冒泡到代码中的更高点,然后你可以处理错误。这使您可以完全免除这些检查。
答案 2 :(得分:1)
好问题!在StackOverflow中,很少有问题询问“原理背后的哲学”。
关于EAFP definition in Python glossary,我什至更进一步地说,在这种情况下,提及“如果假设被证明是错误的,则发生缓存异常”。因为,让我们面对现实,下面的第二个代码片段看起来并不“干净快捷”(在上述定义中使用的术语)。难怪OP问了这个问题。
# LBYL
if key in dic:
print(dic[key])
else:
handleError()
# EAFP
try:
print(dic[key])
except KeyError:
handleError()
我要说的是,EAFP大放异彩的真正时刻是,您根本不会编写try ... except ...
,至少在大多数基础代码中都不会。因为the first rule of exception handling: do not do exception handling。考虑到这一点,现在,让我们将第二个片段重写为:
# Real EAFP
print(dic[key])
现在,不是真正的EAFP方法干净快捷吗?
答案 3 :(得分:0)
我将扩展@RayLuo 的回答。
LBYL 的问题是,它实际上通常不起作用。除非你有一个单线程应用程序,否则总是有可能的竞争条件:
# LBYL
if key in dic:
# RACE CONDITION
print(dic[key])
else:
handleError()
# EAFP
try:
print(dic[key])
except KeyError:
handleError()
可以在 if
检查和 print
之间的字典中添加一个键。在这种特殊情况下,这听起来不太可能。但是,如果您进行的检查是针对数据库、API 或任何可以与您的应用程序数据异步更改的外部数据源进行的,则更有可能。
这意味着实施 LBYL 的“正确”方式是:
if key in dic:
try:
print(dic[key])
except KeyError:
handleError()
else:
handleError()
注意 try
/ except
子句与
EAFP 方法。
由于即使使用 LBYL 方法也必须处理 EAFP 样式的异常,因此您不妨首先使用 EAFP 方法。
我使用 if
检查的唯一情况是接下来的操作(在本例中为 print
)是否启动起来非常昂贵/耗时。这种情况很少见,也不是每次都使用 if
检查的理由。
底线:LBYL 在一般情况下不起作用,但 EAFP 起作用。优秀的开发人员专注于通用解决方案模式,他们可以自信地在各种问题中使用。学习始终如一地使用 EAFP。