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

2025-01-22 08:45:00
admin
原创
91
摘要:问题描述:我应该测试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大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2793  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1710  
  PLM系统在企业项目管理中扮演着至关重要的角色,尤其是在项目采购管理方面,能够通过一系列策略提升采购效率、降低成本并保障质量。通过深入解析相关策略,企业可以更好地利用PLM系统优化采购流程,实现项目的顺利推进与整体目标的达成。需求精准定义策略在项目采购中,明确需求是首要任务。PLM系统可助力企业精准定义采购需求。首先,...
plm是什么意思   0  
  在企业的运营过程中,跨部门数据共享一直是一个关键且颇具挑战的问题。不同部门之间由于业务差异、系统不兼容等多种因素,常常形成信息孤岛,导致数据无法顺畅流通,影响企业整体的决策效率和协同效果。而 PLM 系统作为一种先进的管理工具,为解决这一难题提供了有效的途径。通过其一系列强大的核心功能,能够打破部门之间的数据壁垒,实现...
plm系统   0  
  PLM(产品生命周期管理)项目涉及产品从概念设计到退役的全流程管理,其复杂性和长期性要求高效的项目进度管理工具。甘特图作为一种直观且实用的项目进度可视化工具,在PLM项目中发挥着关键作用。通过甘特图,项目团队成员能够清晰地了解项目任务的时间安排、进度状态以及各项任务之间的关系,从而更好地协调工作、分配资源,确保项目按计...
plm流程是什么   0  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

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

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

内置subversion和git源码管理

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

免费试用