error-handling - 复杂功能的Pythonic错误处理

标签 error-handling python

我想知道长期运行的函数中是否存在处理错误的Python方式,这种错误可能部分地影响了函数的继续执行能力。

例如,考虑一个给定URL列表的函数,该函数以递归方式检索顶级URL路径下的资源和所有链接的资源。它将检索到的资源存储在本地文件系统中,该文件系统具有与URL结构类似的目录结构。本质上,这是页面列表的基本递归wget。

该功能可能会在很多方面失败:

  • URL可能无效或无法解析的
  • 主机可能无法访问(可能是暂时的)
  • 本地保存可能存在磁盘错误
  • 您还能想到的其他任何东西。

  • 检索或保存任何一种资源失败只会影响函数继续处理该资源以及可能与之链接的任何子资源的能力,但是可以继续检索其他资源。

    错误处理的简单模型是,在第一个错误时,会引发适当的异常以供调用者处理。问题在于它终止了该功能并且不允许其继续运行。该错误可能已修复,并且该功能从头开始重新启动,但这将导致工作重做,并且任何永久性错误都可能意味着我们永远无法完成。

    我想到的一些替代方法是:
  • 在发生错误时将其记录在列表中,并中止任何子资源对该资源的处理,但继续进行下一个资源。如果出现太多错误,则可以使用阈值来中止整个功能,或者只是尝试一切。调用者可以在函数完成时查询此列表,以查看是否存在任何问题。
  • 调用者可以提供一个可调用对象,每个错误都会调用该对象。这将记录错误的责任移回了调用者。您甚至可以指定,如果可调用对象返回False,则该处理应停止。这会将阈值管理移至调用方。
  • 用后者实现前者,提供一个错误处理对象,而不是对前者的行为进行编码。

  • 在Python讨论中,我经常注意到某些描述为Pythonic或非Pythonic的方法。我想知道是否有任何特别的Python方法来处理上述场景类型。

    Python是否附带了比异常处理的终止模型更复杂的错误处理模型,或者是否更复杂的电池使用了我应该复制以保持Pythonic的错误处理模型?

    注意:请不要专注于示例。我不是要解决该特定领域中的问题,但是这似乎是一个很好的例子,大多数人在这里都可以理解。

    最佳答案

    我认为您在此处所讨论的级别没有特别明显的“Pythonic/非Pythonic”区别。

    在此领域中没有“一刀切”的重要原因之一是,您想要的确切语义将是针对特定问题的。

  • 对于一种情况,第一次失败中止可能就足够了。
  • 另外,如果任何操作失败,则可能需要中止和回滚。
  • 第三,您可能想要完成尽可能多的操作,并简单地记录并忽略故障
  • 对于第四个替代方案,您可能希望尽可能多地完成,但是在最后提出异常以报告任何失败的事件。

  • 甚至支持错误处理程序也不一定能涵盖所有这些所需的行为-一个简单的每次故障错误处理程序也无法轻松提供中止和回滚语义,或者最终不会产生单个异常。 (这并非不可能-您只需要弄懂诸如将绑定(bind)方法或闭包作为错误处理程序传递之类的技巧即可)

    因此,您最好的办法是对典型的使用场景和遇到错误时的预期行为进行有根据的猜测,然后相应地设计API。

    完全通用的解决方案将接受一个错误处理程序,该错误处理程序会在发生每个错误时得到处理,而最终的“错误已发生”处理程序将为调用者提供一个机会来决定如何处理多个错误(使用某种协议(protocol)允许数据被处理)。从各个错误处理程序传递到最终批处理错误处理程序)。

    但是,提供这种通用解决方案可能是API设计失败。 API的设计者应该对如何使用他们的API以及应该如何处理错误有意见。要记住的主要事情是不要过度设计您的解决方案:
  • 如果幼稚的方法足够,请不要乱用
  • 如果在列表中收集失败并报告单个错误就足够了,请执行
  • 如果您需要在某部分失败的情况下回滚所有内容,则可以通过
  • 这样的方式来实现
  • 如果存在用于自定义错误处理的真正用例,则接受错误处理程序作为API的一部分。但是,在执行此操作时请牢记特定的用例,不要仅仅为了它而这样做。并且在您这样做时,如果用户未指定一个明智的默认处理程序,则该处理程序将被使用(这可能只是幼稚的“立即筹集”方法)
  • 如果您确实提供了可选的错误处理程序,请考虑提供一些标准的错误处理程序,这些错误处理程序可以作为可调用项或命名字符串(即,按照文本编解码器的错误处理程序选择的方式)进行传递。

    作为一般原则,您可能会得到的最好的结果是,“Pythonic”错误处理将尽可能简单,但没有那么简单。但是到那时,这个词只是被用作“好代码”的同义词,这实际上并不是它的意图。

    另一方面,谈论非Pythonic错误处理可能采用的实际形式稍微容易一些:
    def myFunction(an_arg, error_handler)
      # Do stuff
      if err_occurred:
        if isinstance(err, RuntimeError):
          error_handler.handleRuntimeError()
        elif  isinstance(err, IOError):
          error_handler.handleIOError()
    

    Python的惯用法是,如果根本支持错误处理程序,那么它们只是简单的可调用对象。给他们提供他们决定如何处理情况所需的信息,而不是代表他们做出太多决定。如果要使实现错误处理的常见方面更容易,请提供一个单独的帮助程序类,该类具有进行分派(dispatch)的__call__方法,以便人们可以决定是否要使用它(或要使用多少)。当他们确实使用它时覆盖)。这并不完全是特定于Python的,但这是来自语言的人的烦人之处,这使得烦人地难以传递任意可调用对象(例如Java,C,C++)可能会出错。因此,复杂的错误处理协议(protocol)无疑是进入“非Pythonic错误处理”领域的一种方式。

    上面的非Pythonic代码中的另一个问题是没有提供默认处理程序。迫使每个API用户做出他们可能还没有能力做出的决定,只是API设计不佳。但是现在我们回到了一般的“好代码”/“坏代码”领域,因此实际上不应使用Pythonic/非Pythonic来描述两者之间的区别。

  • 关于error-handling - 复杂功能的Pythonic错误处理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5535421/

    相关文章:

    python-3.x - 如何处理ValueError?

    python - 将python数据框转换为列表

    类中函数中的Python列表

    python - 如何在python while循环中切换函数

    python - 关于如何为 Pygments 编写词法分析器的大量文档?

    PHP通知和警告的区别

    error-handling - Vim 脚本 : How to easily pipe data into the cwindow

    r - 如何在R中的for循环中仅跳过特定类型的错误?

    c - 如何为以下表达式进行类型转换

    Python & Couchbase 锁定/解锁机制