【问题标题】:Exception Handling: What level to put it at异常处理:放在什么级别
【发布时间】:2015-06-05 20:35:10
【问题描述】:

这是一个一般性和概念性的问题,不确定它是否在 SO 的范围内,但我会试一试。

我曾多次遇到类似以下情况,但一直不知道如何处理。

我有几个函数在某些层次结构中相互调用。我想处理函数的错误输入,但有些函数永远不会直接调用其他函数。

比如我有这个功能:

def getDataSourcesOfTypes(username, datatypes):
    if len(datatypes) < 1:
        raise ValueError("Please specify at least 1 datatype")
    http = authorizeHttp(username)
    ...snip...
    return data_sources_list

这叫:

def authorizeHttp(username):
    if not isinstance(username, str):
        raise ValueError("Invalid username.")
    credential_json = Database.getCredential(username)
    ...snip...      

这又叫这个:

def getCredential(username):
    Database.connection.row_factory = sqlite.Row  # @UndefinedVariable
    cursor = Database.connection.cursor()
    ...snip...
    result = cursor.fetchone()
    if result is None:
        raise LookupError("Username does not exist.")
    else:
       ...snip...

现在,对于username,我有几个机会抓住一个坏的(即不是字符串、不存在等)。显然,直到我调用最后一个函数,我才能捕捉到它没有出现在数据库中的错误,但我可以在第一个函数之前捕捉到None

问题是:捕捉异常的最佳位置在哪里?在层次结构的最底层?尽快?

再次,我认为这对于 SO 来说可能是一个过于主观的问题,但我真的很好奇什么被认为是最佳实践。

【问题讨论】:

    标签: python python-2.7 exception


    【解决方案1】:

    有一些一般性的建议可以遵循。

    • 在可以修复的地方发现错误
    • 即使可能也不要忽略异常(没有except: pass
    • 记录它们
    • 不要使用过于宽泛的情况,例如except:except Exception:
    • 对不同的异常类型使用不同的异常块
    • 在需要时使用elsefinally
    • 异常是一种异常行为,不要让它合乎逻辑 应用的一部分

    这是一个关于异常处理的good articlePython docs 也有最佳实践。还有一个from the past(但仍然有有效信息)。

    考虑捕捉它们的地方。好吧,这里有不同的可能性。

    1. 您不会在代码的其他任何地方使用这些函数。
      在这种情况下,以“我需要通知用户出了什么问题。仅此而已”的意图来看待这个问题。在这种情况下,可以在最高级别捕获异常。

    2. 当这些函数在代码中被广泛使用时,您需要一种不同的方法。像你做的那样引发异常,并在地方捕获它们,如果这是所需的行为,可以做任何事情来返回正常的程序执行。或者,有时需要重新引发异常。例如:

      try:
           credential_json = Database.getCredential(username)
      except LookupError as le:
           log.warning(le)
           renew_token()  # (example) try to fix the problem somehow
           credential_json = Database.getCredential(username) # retry
           # note, that the next possible LookupError is not handled here.
      finally:
           check_data(credential_json)
      return credential_json
      
    3. 您正在编写可重用的代码。在这种情况下,它很复杂。一个 需要深入了解您的工作流程。您的代码必须 提供自定义异常、异常处理程序,可以轻松 覆盖,控制返回值,提供回退等等。

      def function_with_handler(data, error_handler=_handler, *args, **kwargs):
          try:
              do_stuff()
          except CustomError as such_problem:
              # log.level(such_problem)
              _handler(such_problem, data, *args, **kwargs)
      

    此外,有时向fail_silently = True 添加一个选项实际上是个好主意。

    阅读大型项目的源代码通常是一种好习惯。例如,看看Django 是如何处理exceptions 的。

    【讨论】:

    • “异常是一种异常行为”——用其他语言表示。在 Python 中,建议“请求原谅比请求许可更好”,它避免了讨厌的 TOCTOU 漏洞
    • 是的,当使用try:except 代替检查时,竞争条件是一个很好的例子。
    • “不要使用太宽泛的情况,比如 except: 或 except Exception” 通常一个特定的 API 会抛出十几个可能的异常,而你的代码需要如何处理并没有什么区别甚至可以响应,除了以一般方式处理故障并传达/记录特定类型的异常和与之相关的消息详细信息以进行故障排除。我不想编写十几个不同的异常块,它们基本上都做同样的事情。仅当您的代码对它们的响应不同时,才应使用不同的块。
    • @wojtow,我想说except (ValueError, CustomError, ..) as e: 在这种情况下更可取。它明确显示了您期望的异常。
    【解决方案2】:

    我会说,作为一般规则,最接近错误的真正来源,可以(可能)适当地处理它。如果这是一个错误的输入,那么在输入的级别(可能会以某种方式链接它)。如果这是字典中缺少键的预期异常,请立即捕获它。等等

    当然,还有更多。

    【讨论】:

      猜你喜欢
      • 2010-10-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-01
      相关资源
      最近更新 更多