【问题标题】:Avoid exceptions?避免异常?
【发布时间】:2010-09-29 09:36:46
【问题描述】:

此特定示例与 Python 中的 Django 相关,但应适用于任何支持异常的语言:

try:
    object = ModelClass.objects.get(search=value)
except DoesNotExist:
    pass

if object:
    # do stuff

Django 模型类提供了一个简单的方法 get,它允许我从数据库中搜索 唯一一个 对象,如果它或多或少找到它会引发一个例外。如果可以使用另一种 filter 方法找到零个或多个,该方法返回一个列表:

objects = ModelClass.objects.filter(search=value)
if len(objects) == 1:
    object = objects[0]
    # do stuff

我是否过度厌恶异常? 对我来说,异常似乎有点浪费,我猜测,四分之一到二分之一的时间将是“异常”。我更喜欢在失败时返回 None 的函数。使用 Django 的 filter 方法并自己处理列表会更好吗?

【问题讨论】:

    标签: python django exception


    【解决方案1】:

    我不同意上述 cmets 的观点,即在这种情况下异常是低效的,尤其是因为它被用于 I/O 绑定操作。

    这是一个使用 Django 和内存中 sqlite 数据库的更实际的示例。运行 100 个不同查询中的每一个,然后对 100 个运行中的每一个进行平均。虽然我怀疑这是否重要,但我也改变了执行顺序。

    With ObjectDoesNotExist... 0.102783939838
    Without exception ........ 0.105322141647
    
    With ObjectDoesNotExist... 0.102762134075
    Without exception ........ 0.101523952484
    
    With ObjectDoesNotExist... 0.100004930496
    Without exception ........ 0.107946784496
    

    您可以在自己的 Django 环境中对此进行检测,但我怀疑您是否将时间花在避免此异常上。

    【讨论】:

      【解决方案2】:

      信不信由你,这实际上是一个在每种语言中都有所不同的问题。在 Python 中,语言本身不会异常的事件会定期抛出异常。因此,我认为“你应该只在特殊情况下抛出异常”规则并不完全适用。不过,考虑到大量 .Net 程序员(请参阅this question)了解更多信息,我认为您在此论坛上获得的结果将倾向于这种观点。

      至少,我最好不要抓住任何坚持该规则的人,他们曾经在 Python 中使用生成器或 for 循环(两者都涉及在非异常情况下抛出异常)。

      【讨论】:

      • +1:Python 在某些非异常情况下使用异常。如果它保持简单,简单胜于复杂(即引发 StopIteration)。
      【解决方案3】:

      我同意另一个答案,但我想添加这样的异常传递会给您带来非常明显的性能影响。强烈建议您检查结果是否存在(如果这是过滤器所做的)而不是传递异常。


      编辑:

      为了响应对此的数字请求,我运行了这个简单的测试......

      import time
      
      def timethis(func, list, num):
        st=time.time()
        for i in xrange(0,1000000):
          try:
            func(list,num)
          except:
            pass
        et = time.time()
        print "Took %gs" % (et-st)
      
      def check(list, num):
        if num < len(list):
          return list[num]
        else:
          return None
      
      a=[1]
      timethis(check, a, 1)
      timethis(lambda x,y:x[y], a, 1)
      

      输出是..

      Took 0.772558s
      Took 3.4512s
      

      HTH。

      【讨论】:

      • 你能提供一些比较异常和非异常版本的测量吗?我想看看这个“引人注目”的打击究竟有多大。
      • 作为记录,这两个示例都引发了 StopIteration 异常。 IMO 这表明如果合理使用异常不会对性能产生重大影响。
      • a[1] 会给你 IndexError。迭代继续进行,因为异常被捕获并在循环中传递。我坚持认为我的示例有效地表明循环中传递的异常可能会影响性能(在这种情况下为 5 倍)。
      • 这是不正确的。在问题的上下文中,没有有意义的性能问题。您的测试进行了 100 万次迭代,出现异常的版本耗时 3.5 秒。这意味着每个异常花费大约 0.0035 毫秒。查看测试的另一种方法是,异常只花费比单个 if 语句多 5 倍的成本。如果您有一个非常大、快速的循环,那么异常可能会导致性能问题。如果您对 HTTP 请求执行几个数据库查询,则允许每个查询引发异常对性能的影响基本上为零。
      【解决方案4】:

      编程语言在使用异常方面存在很大分歧。

      • 大多数人的观点是例外应该是例外。例如,在大多数带有异常的语言中,通过异常进行的控制转移比通过过程返回要昂贵得多。

      • 有一种强烈的少数观点认为异常只是另一种控制流结构,它们应该很便宜。 Standard ML of New JerseyObjective Caml 编译器订阅该视图。如果您有便宜的异常,您可以编写一些花哨的回溯算法,使用其他机制更难以干净地编码。

      我已经看到这种关于新语言设计的辩论多次重复,而且几乎总是获胜者认为例外应该是昂贵且罕见的。当您关心性能时,明智的做法是在编程时牢记这一点。

      【讨论】:

        【解决方案5】:

        对异常的厌恶是一个见仁见智的问题 - 但是,如果有理由相信一个函数或方法将被多次调用或被快速调用,异常将导致显着减速。我从我的previous question 学到了这一点,我之前依靠抛出的异常来返回默认值,而不是进行参数检查来返回该默认值。

        当然,由于任何原因,异常仍然可能存在,如果有必要,您不应该害怕使用或抛出异常——尤其是那些可能破坏调用函数正常流程的异常。

        【讨论】:

          【解决方案6】:

          线索就在名字里——例外应该是例外。

          如果您总是希望该项目存在,请使用get,但如果您希望它在合理比例的时间内不存在(即它不存在是预期结果而不是异常结果),那么我会建议使用filter

          因此,正如您所指出的那样,预计不会存在 1 比 2 和 1 比 4,我肯定会在 filter 周围编写一个包装器,因为这绝对不是一个例外情况。

          【讨论】:

            【解决方案7】:

            答案将取决于代码的意图。 (我不确定您的代码示例是什么意思,例外情况下的传递令人困惑,其余代码将如何处理 object 变量?)

            在许多情况下,是否使用异常或使用将情况视为非异常的方法是一个品味问题。当然,如果 except 子句中的实际代码与您必须使用以避免异常的过滤器方法一样复杂,那么请使用过滤器方法。更简单的代码就是更好的代码。

            【讨论】:

              猜你喜欢
              • 2011-05-30
              • 2017-02-10
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-09-15
              • 2016-08-13
              • 2012-01-31
              相关资源
              最近更新 更多