我不知道为什么我们最终需要尝试……最后陈述。在我看来,这个代码块

try:
    run_code1()
except TypeError:
    run_code2()
other_code()

和这个finally的用法一样:

try:
    run_code1()
except TypeError:
    run_code2()
finally:
    other_code()

我遗漏了什么吗?


当前回答

正如文档中所解释的,finally子句旨在定义在所有情况下都必须执行的清理操作。

如果finally存在,则指定一个' cleanup '处理程序。的尝试 子句,包括except和else子句。如果一个 异常发生在任何子句中而不被处理,则 异常被暂时保存。finally子句被执行。如果 有一个保存的异常,它在finally结束时被重新引发 条款。

一个例子:

>>> def divide(x, y):
...     try:
...         result = x / y
...     except ZeroDivisionError:
...         print("division by zero!")
...     else:
...         print("result is", result)
...     finally:
...         print("executing finally clause")
...
>>> divide(2, 1)
result is 2.0
executing finally clause
>>> divide(2, 0)
division by zero!
executing finally clause
>>> divide("2", "1")
executing finally clause
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in divide
TypeError: unsupported operand type(s) for /: 'str' and 'str'

如您所见,finally子句在任何事件中都被执行。由分隔两个字符串引发的TypeError不由except子句处理,因此在finally子句执行后重新引发。

在实际应用程序中,finally子句用于释放外部资源(例如文件或网络连接),而不管资源的使用是否成功。

其他回答

代码块是不等效的。如果run_code1()抛出TypeError以外的异常,或者run_code2()抛出异常,finally子句也将运行,而第一个版本中的other_code()在这些情况下不会运行。

如果你早点回来,情况就不一样了:

try:
    run_code1()
except TypeError:
    run_code2()
    return None   # The finally block is run before the method returns
finally:
    other_code()

与此相比:

try:
    run_code1()
except TypeError:
    run_code2()
    return None   

other_code()  # This doesn't get run if there's an exception.

其他可能导致差异的情况:

如果在except块内抛出异常。 如果在run_code1()中抛出异常,但它不是TypeError。 其他控制流语句,如continue和break语句。

您可以使用finally来确保无论是否发生异常都关闭或释放文件或资源,即使您没有捕获异常。(或者如果你没有捕捉到特定的异常。)

myfile = open("test.txt", "w")

try:
    myfile.write("the Answer is: ")
    myfile.write(42)   # raises TypeError, which will be propagated to caller
finally:
    myfile.close()     # will be executed before TypeError is propagated

在本例中,您最好使用with语句,但这种结构也可以用于其他类型的资源。

几年后,我写了一篇关于滥用finally的博客文章,读者可能会觉得很有趣。

Finally也可以用于在运行主要工作的代码之前运行“可选”代码,而可选代码可能由于各种原因而失败。

在下面的例子中,我们不知道store_some_debug_info会抛出什么样的异常。

我们可以运行:

try:
  store_some_debug_info()
except Exception:
  pass
do_something_really_important() 

但是,大多数lint会抱怨捕捉到的异常过于模糊。此外,由于我们选择只传递错误,except块并没有真正增加值。

try:
  store_some_debug_info()
finally:
  do_something_really_important()     

上面的代码与第一个代码块具有相同的效果,但更简洁。

它们不相等。最后,不管发生什么,代码都会运行*。 它对于清理必须运行的代码非常有用。


*: 正如Mark Byers所评论的,任何导致进程立即终止的事情也会阻止最终代码的运行。 后者可以是os._exit()。或断电,但无限循环或其他东西也属于这一类。