最好“尝试”某事并捕获异常或测试是否有可能先避免异常?

2025-01-22 08:45:00
admin
原创
69
摘要:问题描述:我应该测试if某些东西是否有效还是仅仅try执行它并捕获异常?是否有确凿的文献证明哪种方式是首选?有没有一种更加Pythonic 的方法?例如,我应该:if len(my_list) >= 4: x = my_list[3] else: x = 'NO_ABC' 或者:try:...

问题描述:

我应该测试if某些东西是否有效还是仅仅try执行它并捕获异常?

  • 是否有确凿的文献证明哪种方式是首选?

  • 有没有一种更加Pythonic 的方法?

例如,我应该:

if len(my_list) >= 4:
    x = my_list[3]
else:
    x = 'NO_ABC'

或者:

try:
    x = my_list[3]
except IndexError:
    x = 'NO_ABC'

一些想法...

PEP 20说:

错误不应该默默地传递。

除非明确地禁止。

使用 atry而不是 an是否应if被解释为错误悄悄传递?如果是这样,您是否通过这种方式明确地将其静音,从而使其正常?


我指的并不是只能用一种方式做事的情况;例如:

try:
    import foo
except ImportError:
    import baz

解决方案 1:

如果try/except结果为if/else

  • 加速(例如通过防止额外的查找)

  • 更简洁的代码(更少的行数/更易读)

通常,这些是相辅相成的。


加速

尝试通过以下方式在长列表中查找元素:

try:
    x = my_list[index]
except IndexError:
    x = 'NO_ABC'

当 可能在列表中并且通常不会引发 IndexError 时,try, except 是最佳选择index。这样,您就无需通过 进行额外查找if index < len(my_list)

Python 鼓励使用异常,您可以处理异常,这是Dive Into Python中的一句话。您的示例不仅 (优雅地) 处理异常,而不是让它默默地通过,而且异常仅在未找到索引的特殊情况下发生 (因此有异常一词!)。


更清洁的代码

Python 官方文档提到了EAFP:请求原谅比请求许可更容易,Rob Knight指出,捕获错误而不是避免错误,可以使代码更干净、更易于阅读。他的例子是这样的:

更糟的情况(LBYL‘三思而后行’)

#check whether int conversion will raise an error
if not isinstance(s, str) or not s.isdigit():
    return None
elif len(s) > 10:    #too many digits for int conversion
    return None
else:
    return int(s)

更好(EAFP:请求原谅比请求许可更容易)

try:
    return int(s)
except (TypeError, ValueError, OverflowError): #int conversion failed
    return None

解决方案 2:

在这种特殊情况下,你应该使用完全不同的方法:

x = myDict.get("ABC", "NO_ABC")

但一般来说:如果您预计测试会频繁失败,请使用if。如果测试相对于尝试操作并在失败时捕获异常而言成本较高,请使用try。如果这两个条件都不适用,请选择更容易理解的。

解决方案 3:

如果存在任何竞争条件的可能性,则应始终直接使用tryandexcept而不是在保护内部使用。例如,如果您想确保目录存在,请不要这样做:if

import os, sys
if not os.path.isdir('foo'):
  try:
    os.mkdir('foo')
  except OSError, e
    print e
    sys.exit(1)

isdir如果另一个线程或进程在和之间创建目录mkdir,您将退出。相反,请执行以下操作:

import os, sys, errno
try:
  os.mkdir('foo')
except OSError, e
  if e.errno != errno.EEXIST:
    print e
    sys.exit(1)

仅当无法创建“foo”目录时才会退出。

解决方案 4:

如果在执行某项操作之前检查它是否会失败很简单,那么您可能应该支持这样做。毕竟,构造异常(包括其相关的回溯)需要时间。

以下情况应使用例外:

  1. 意想不到的事情,或者……

  2. 您需要跳过多个逻辑层次的事情(例如,当 abreak不能让您走得足够远时),或者......

  3. 您无法提前知道具体要处理什么异常,或者......

  4. 提前检查失败的成本很高(相对于尝试操作而言)


请注意,很多时候,真正的答案是“都不是” - 例如,在您的第一个例子中,您真正应该做的只是使用.get()来提供默认值:

x = myDict.get('ABC', 'NO_ABC')

解决方案 5:

正如其他帖子提到的,这取决于具体情况。使用 try/except 代替提前检查数据的有效性存在一些危险,尤其是在较大的项目中使用它时。

  • 在捕获到异常之前,try 块中的代码可能会造成各种破坏 - 如果您事先主动使用 if 语句进行检查,则可以避免这种情况。

  • 如果在 try 块中调用的代码引发了常见的异常类型,例如 TypeError 或 ValueError,则您可能实际上无法捕获到您期望捕获的相同异常 - 可能是在到达可能引发异常的行之前或之后引发相同异常类的其他东西。

例如,假设你有:

try:
    x = my_list[index_list[3]]
except IndexError:
    x = 'NO_ABC'

IndexError 没有说明它是否在尝试获取 index_list 或 my_list 的元素时发生。

解决方案 6:

使用 try 而不是 if 是否应被解释为错误悄悄传递?如果是这样,您是否通过这种方式明确地将其静音,因此可以吗?

使用try表示承认错误可能会通过,这与默默地让错误通过相反。使用except表示根本不让错误通过。

try: except:在逻辑比较复杂的情况下,优先使用if: else:。简单比复杂好;复杂比复杂好;请求原谅比请求许可更容易。

“错误绝不应默默传递”所警告的情况是,代码可能会引发您知道的异常,并且您的设计承认这种可能性,但您没有设计出处理异常的方法。在我看来,明确地抑制错误就像pass在块中执行某些操作except,只有在理解“什么都不做”才是特定情况下正确的错误处理的情况下才能这样做。(这是我觉得在编写良好的代码中可能确实需要注释的少数几次之一。)

但是,在您的特定示例中,两者都不合适:

x = myDict.get('ABC', 'NO_ABC')

每个人都指出这一点的原因——即使你承认你想要总体了解,并且无法想出更好的例子——是在很多情况下实际上存在等效的回避步骤,寻找它们是解决问题的第一步。

解决方案 7:

每当你使用try/except控制流时,问自己:

  1. 是否容易看出何时try区块成功,何时区块失败?

  2. 您是否了解块内的所有副作用try

  3. 您是否知道块抛出异常的所有情况?try

  4. 如果块的实现try发生变化,你的控制流是否仍会按预期运行?

如果对其中一个或多个问题的答案是“否”,那么您可能需要请求很多宽恕;最有可能的是来自您未来的自己。


举个例子。我最近在一个较大的项目中看到了如下代码:

try:
    y = foo(x)
except ProgrammingError:
    y = bar(x)

与程序员交谈后发现,预期的控制流是:

如果 x 是整数,则执行 y = foo(x)。

如果 x 是整数列表,则执行 y = bar(x)。

这是有效的,因为进行了数据库查询,如果是整数,foo则查询会成功,如果是列表,则抛出。x`ProgrammingError`x

在这里使用try/except是一个糟糕的选择:

  1. 异常的名称ProgrammingError并未透露实际问题(不是x整数),这使得很难看出发生了什么。

  2. 在数据库调用期间引发,这很浪费时间。如果在数据库引发异常之前写入某些内容,或者更改其他系统的状态,ProgrammingError事情就会变得非常糟糕。foo

  3. 目前尚不清楚是否ProgrammingError仅当x是整数列表时才会引发。例如,假设foo的数据库查询中有一个拼写错误。这也可能引发ProgrammingError。结果是bar(x)现在当 是整数时也会调用x。这可能会引发神秘的异常或产生不可预见的结果。

  4. try/except块为 的所有未来实现添加了一项要求foo。每当我们更改 时foo,我们现在都必须考虑它如何处理列表,并确保它抛出ProgrammingError而不是AttributeError或根本没有错误。

解决方案 8:

要了解一般含义,您可以考虑阅读《Python 中的习语和反习语:异常》。

在您的特定情况下,正如其他人所述,您应该使用dict.get()

获取(键[,默认])

如果 key 在字典中,则返回 key 的值,否则返回默认值。如果没有给出默认值,则默认为 None,这样此方法就不会引发 KeyError。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用