为什么要求宽恕比获得许可更容易?#34;在Python?

时间:2015-10-02 06:50:34

标签: python coding-style

为什么#34;比获得许可更容易请求宽恕" (EAFP)认为Python的良好做法?作为编程新手,我的印象是,与使用其他检查相比,使用许多try...except例程会导致代码膨胀且可读性较差。

EAFP方法的优势是什么?

注意:我知道这里有类似的问题,但它们主要是指一些具体的例子,而我对这一原则背后的哲学更感兴趣。

4 个答案:

答案 0 :(得分:4)

LBYLEAFP的计数器方法与断言没有任何关系,它只是意味着在您尝试访问可能不存在的内容之前添加一个检查。

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。