【问题标题】:"except" statement with same exception class as parameter twice两次具有与参数相同的异常类的“except”语句
【发布时间】:2012-05-07 06:27:07
【问题描述】:

在 Python 中,如何在 try/except 语句中两次使用具有相同异常名称的 except 块,而无需将代码包装到另一个 try/except 块中?

简单示例(此处每个调用pages.get 可能引发异常):

try:
    page = pages.get(lang=lang)
except Page.DoesNotExist:
    if not lang == default_lang:
        page = pages.get(lang=default_lang)
    else:
        raise Page.DoesNotExist
except Page.DoesNotExist:
    page = pages[0]

目前,在我的 Django 应用程序中,我会这样处理(但我不想在这里“额外”try 块):

try:
    try:
        page = pages.get(lang=lang)
    except Page.DoesNotExist:
        if not lang == default_lang:
            page = pages.get(lang=default_lang)
        else:
            raise Page.DoesNotExist
except Page.DoesNotExist:
    page = pages[0]

感谢任何比上面更好的处理代码! :)

谢谢。

【问题讨论】:

  • 你为什么要这样做?
  • 你不能。也许您可以告诉我们您认为需要这样做的原因,以便我们告诉您为什么不需要这样做
  • 我同意,我认为这不是一个好主意。这意味着您有两个由同一事物触发的代码路径,它们执行不同的操作。将 try/catch 视为 if/else 语句。
  • 真的很好奇不问:为什么不把page = pages[0] 而不是raise Page.DoesNotExist 放在第一个try 块中? (当然这会帮助我更好地理解这个问题)
  • @Gabber each pages.get 调用可能会引发异常,所以最后一个except 语句只是尝试捕获这个可能的异常

标签: python exception syntax django-orm


【解决方案1】:

虽然,我目前无法为您的用例找到更好的方法,但 Python 的 try/except 语句有一些额外的功能:

try:
  pass
  # run your code
except:
  pass
  # run your error handling, etc.. 
else:
  pass
  # is run whenever an exception didn't happen
finally:
  pass
  # will be executed always (good for cleaning up)

【讨论】:

    【解决方案2】:

    您也不能这样做并期望 elif 执行:

    if foo == bar:
      # do "if"
    elif foo == bar:
      # do "elif"
    

    真的没有理由这样做。对于您的except 关注也是如此。

    这是您的第一个代码 sn-p 的反汇编 Python 字节码:

     13           0 SETUP_EXCEPT            10 (to 13)
    
     14           3 LOAD_GLOBAL              0 (NameError)
                  6 RAISE_VARARGS            1
                  9 POP_BLOCK           
                 10 JUMP_FORWARD            44 (to 57)
    
     15     >>   13 DUP_TOP             
                 14 LOAD_GLOBAL              0 (NameError)
                 17 COMPARE_OP              10 (exception match)
                 20 POP_JUMP_IF_FALSE       35
                 23 POP_TOP             
                 24 POP_TOP             
                 25 POP_TOP             
    
     16          26 LOAD_GLOBAL              0 (NameError)
                 29 RAISE_VARARGS            1
                 32 JUMP_FORWARD            22 (to 57)
    
     17     >>   35 DUP_TOP             
                 36 LOAD_GLOBAL              0 (NameError)
                 39 COMPARE_OP              10 (exception match)
                 42 POP_JUMP_IF_FALSE       56
                 45 POP_TOP             
                 46 POP_TOP             
                 47 POP_TOP             
    
     18          48 LOAD_CONST               1 (1)
                 51 PRINT_ITEM          
                 52 PRINT_NEWLINE       
                 53 JUMP_FORWARD             1 (to 57)
            >>   56 END_FINALLY         
            >>   57 LOAD_CONST               0 (None)
                 60 RETURN_VALUE        
    

    很明显,第一个COMPARE_OPNameError(在偏移量 17 处)将捕获异常并跳转到第二个这样的比较之后(在偏移量 36 处)。

    【讨论】:

      【解决方案3】:

      我建议你创建一个函数来为你获取页面,例如。像这样:

      def get_page(language):
          if language == default_lang:
              lang_list = [language]
          else:
              lang_list = [language, default_lang]
      
          for lang in lang_list:
              try:
                  return pages.get(lang=lang)
              except Page.DoesNotExist:
                  pass
      
          return pages[0]
      

      【讨论】:

      • @neoascetic 您也可以只使用for lang in set(language, default_lang) 而不是前四行。您还应该将pages 传递给函数。
      • @detly 集合是无序的,default_lang 应该最后尝试,所以一个列表是合适的。
      【解决方案4】:

      Python 或任何 Sane 语言中的异常永远不会按照您的预期工作。每次引发异常时,它都应追溯堆栈/范围。在当前范围内引发的任何异常每个堆栈/范围只能处理一次。 可以这样想,每个作用域都有一个异常处理机制,带有一个过滤器函数,它可以简单地将异常过滤为匹配提到的顺序。如果发生匹配,则按照指定的异常处理程序进行处理。如果任何异常被重新抛出或生成,它应该由下一个异常处理机制处理,该机制直接到下一个作用域。

      查看示例,我想知道您为什么要通过引发另一个异常而不是在 else 块中处理它来使事实复杂化

      try:
          page = pages.get(lang=lang)
      except Page.DoesNotExist:
          if not lang == default_lang:
              page = pages.get(lang=default_lang)
          else:
              page = pages[0]
      

      【讨论】:

        【解决方案5】:

        except 块中引发的异常不会由同一 try/except 块处理,因此您的示例无法运行。即使可以,您的示例实际上也会引发无限循环,因为第一个 except NameError 只会再次捕获从自身内部引发的异常,然后再次引发 NameError,以此类推。

        此功能是设计使然,否则不可能编写一个异常处理程序来检查异常,然后向外引发异常。 except 块仅处理从 try 块内抛出的异常。如果要处理从 except 块内抛出的异常,则该块需要位于 try 块内。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-07-18
          • 1970-01-01
          • 2013-11-28
          • 2020-07-06
          • 2022-01-17
          • 2014-12-18
          相关资源
          最近更新 更多