为什么在 Python 中“请求宽恕比获得许可更容易”?

n10*_*000 5 python coding-style

为什么“请求宽恕比获得许可更容易”( EAFP ) 被认为是 Python 中的好习惯?作为一名编程新手,我的印象是,try...except与使用其他检查相比,使用许多例程会导致代码臃肿且可读性较差。

EAFP 方法的优势是什么?

NB:我知道这里也有类似的问题,但他们大多是指一些具体的例子,而我更感兴趣的是原理背后的哲学。

alf*_*sin 5

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
Run Code Online (Sandbox Code Playgroud)

LBYL 示例:

if 'snake' in zoo:
    snake = zoo['snake']
else:
    snake = None
Run Code Online (Sandbox Code Playgroud)

  • @brunodesthuilliers 你认为一个抛出一百万个异常并捕获它们的例子是一个很好的例子,可以用作“正常”程序的模型吗?:) (2认同)
  • @brunodesthuilliers 正如我在回答中试图解释的那样,与其他语言相比,在 Python 中捕获异常更便宜,但它当然不是免费的。例外仍然适用于例外情况,在这种情况下,如果您希望密钥大部分时间都存在。然后,LBYL 的额外检查稍微贵一些。 (2认同)
  • @brunodesthuilliers 但它*是*一个廉价的操作。这并不意味着您可以将它与其他更便宜的东西进行比较并说它是错误的。这就像说“打印”很贵,因为不打印会便宜很多。尝试在例如 Java 中运行相同的示例,您可以看到为什么这里异常便宜。 (2认同)

pok*_*oke 5

您在这里混合了两件事:断言和基于 EAFP 的逻辑。

\n\n

断言用于验证函数的契约,即其前置条件和后置条件,有时还有其不变量。它们确保函数按照应有的方式使用。但它们不适用于代码流,因为它们会在出现错误时完全中断执行。一个常见的例子是检查None函数调用中的参数。

\n\n

在 Python 中,通常避免过多使用断言。一般来说,您应该期望代码的用户能够正确使用它。例如,如果您记录了一个函数,该函数接受的参数不是None,那么\xe2\x80\x99 就不需要有一个断言来验证它。相反,只是期望有一个值。如果由于 None 值而出现错误,那么无论如何它都会冒泡,因此用户知道他们做错了什么。但你不应该\xe2\x80\x99 必须一直检查一切。

\n\n

现在,EAFP 有所不同。它\xe2\x80\x99s用于控制流,或者更确切地说,它避免了额外的控制流,有利于期望事情是正确的,并在不正确时捕获异常。显示差异的一个常见示例是字典中的键访问:

\n\n
# LBYL\nif key in dic:\n    print(dic[key])\nelse:\n    handleError()\n\n# EAFP\ntry:\n    print(dic[key])\nexcept KeyError:\n    handleError()\n
Run Code Online (Sandbox Code Playgroud)\n\n

现在这看起来非常相似,尽管您应该记住 LBYL 解决方案会检查字典两次。与捕获异常的所有代码一样,只有在键不存在属于例外情况时才应该执行此操作时才应该执行此操作。因此,如果通常情况下,提供的密钥不在字典中,那么它\xe2\x80\x99s EAFP 并且您应该直接访问它。如果您不希望字典中存在该键,那么您应该首先检查它是否存在(虽然 Python 中的异常更便宜,但它们仍然不是免费的,因此请保留它们以备特殊情况使用)。

\n\n

EAFP 的一个好处还在于,在您的库或应用程序的逻辑深处,key上面),您可以假设此处传递了有效的密钥。因此,您不需要在此处捕获异常,而只需让它们上升到代码中的更高点,然后您就可以在其中处理错误。这使您可以拥有完全不受此类检查的较低级别的功能。

\n