3

在 Python 中,如何except在语句中两次使用具有相同异常名称的块,try/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]

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

谢谢。

4

5 回答 5

3

虽然,我目前无法为您的用例找到更好的方法,但 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)
于 2012-05-07T06:58:57.203 回答
2

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

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

真的没有理由这样做。你的except关心也一样。

这是您的第一个代码片段的反汇编 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_OPto NameError(在偏移 17 处)将捕获异常并在第二个这样的比较之后跳转到(在偏移 36 处)。

于 2012-05-07T06:33:20.937 回答
1

我建议您创建一个功能来为您获取页面,例如。像这样:

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]
于 2012-05-07T08:24:18.807 回答
0

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

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

于 2012-05-07T07:23:07.840 回答
0

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]
于 2012-05-07T06:36:00.150 回答