为什么我们需要Python中的"finally"子句?

RNA*_*RNA 264 python exception-handling try-finally

我不知道为什么我们需要finallytry...except...finally声明中.在我看来,这个代码块

try:
    run_code1()
except TypeError:
    run_code2()
other_code()
Run Code Online (Sandbox Code Playgroud)

与使用finally以下内容相同:

try:
    run_code1()
except TypeError:
    run_code2()
finally:
    other_code()
Run Code Online (Sandbox Code Playgroud)

我错过了什么吗?

Mar*_*ers 369

如果你早点回来,它会有所不同:

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

与此相比:

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

other_code()  # This doesn't get run if there's an exception.
Run Code Online (Sandbox Code Playgroud)

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

  • 如果在except块中抛出异常.
  • 如果抛出异常,run_code1()但它不是TypeError.
  • 其他控制流语句,例如continuebreak语句.

  • 尝试:#x = Hello + 20 x = 10 + 20 除了:打印“我在除了块” x = 20 + 30 else:打印“我在其他块” x += 1 最后:打印“最后 x = % s'%(x) (2认同)
  • 如果`finally`代码中有异常怎么办? (2认同)
  • 这将是一个未捕获的异常,因为没有包装在try中的普通异常-块@HrvojeT除外 (2认同)
  • 哦,这就是原因!这应该在示例中更加清楚地说明。我看到的每个例子都只是一个玩具问题,这些情况都不会出现,我和OP对此感到困惑。 (2认同)

kin*_*all 78

您可以使用它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
Run Code Online (Sandbox Code Playgroud)

在这个例子中,你最好使用该with语句,但这种结构可以用于其他类型的资源.

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


Ant*_*ony 22

他们并不等同.最后,无论发生什么,代码都会运行.它对于必须运行的清理代码很有用.

  • `最后,无论发生什么,代码都会运行......除非有无限循环.或者是电源切割.或者`os._exit()`.要么... (12认同)
  • @Mark实际上,sys.exit会抛出一个正常的异常.但是,是的,导致进程立即终止的任何事情都意味着没有其他任何执行. (3认同)
  • ......一个段错误.或者`SIGABRT`. (2认同)
  • @Stephen 一方面,即使您从 try 块返回,最终代码也会运行。在这种情况下,您将不会遇到 except 子句。 (2认同)

Shu*_*hub 20

这是一段代码来阐明差异:

...

try: 
  a/b
  print('In try block')
  
except TypeError:
  print('In except block')
  
finally: 
  print('In finally block')

print('Outside')
Run Code Online (Sandbox Code Playgroud)

a, b = 0, 1

输出:

In try block 
In finally block 
Outside
Run Code Online (Sandbox Code Playgroud)

(没有错误,除了跳过了块。)


a, b = 1, 0

输出:

In finally block

Traceback (most recent call last):
a/b
ZeroDivisionError: division by zero
Run Code Online (Sandbox Code Playgroud)

(没有为ZeroDivisionError指定异常处理,只执行finally块。


a, b = 0, '1'

输出:

In except block 
In finally block 
Outside
Run Code Online (Sandbox Code Playgroud)

(异常处理得当,程序没有中断。)


注意:如果您有except块来处理所有类型的错误,那么finally块将是多余的。


cap*_*ack 14

要添加到上面的其他答案,finally无论什么都else执行该子句,而只有在未引发异常时才执行该子句.

例如,写入没有异常的文件将输出以下内容:

file = open('test.txt', 'w')

try:
    file.write("Testing.")
    print("Writing to file.")
except IOError:
    print("Could not write to file.")
else:
    print("Write successful.")
finally:
    file.close()
    print("File closed.")
Run Code Online (Sandbox Code Playgroud)

OUTPUT:

Writing to file.
Write successful.
File closed.
Run Code Online (Sandbox Code Playgroud)

如果存在异常,代码将输出以下内容(请注意,将文件保持为只读会导致故意错误.

file = open('test.txt', 'r')

try:
    file.write("Testing.")
    print("Writing to file.")
except IOError:
    print("Could not write to file.")
else:
    print("Write successful.")
finally:
    file.close()
    print("File closed.")
Run Code Online (Sandbox Code Playgroud)

OUTPUT:

Could not write to file.
File closed.
Run Code Online (Sandbox Code Playgroud)

我们可以看到,finally无论异常如何,该子句都会执行.希望这可以帮助.

  • 即使您不使用“ finally”子句也无法解决问题,因为OP希望知道两者之间的区别,所以这还是可行的,一个很好的例子将导致与IOError不同的错误,以表明在将异常传播到调用者之前,将执行finally子句块。 (2认同)
  • 我不知道“其他”是一回事。知道有用。 (2认同)

Sve*_*ach 7

代码块不等同.finally如果run_code1()抛出异常TypeError,或者run_code2()抛出异常,则该子句也将运行,而other_code()在这些情况下不会运行第一个版本.


mha*_*wke 7

在您的第一个示例中,如果run_code1()引发异常而不会发生TypeError什么?...... other_code()不会被执行.

将其与finally:版本进行比较:other_code()无论引发任何异常,都保证执行.


Eug*_*ash 5

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

如果finally存在,则指定“清理”处理程序。该try 子句被执行,包括any exceptelse子句。如果在任何子句中发生异常并且不进行处理,则将临时保存该异常。该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'
Run Code Online (Sandbox Code Playgroud)

如您所见,该finally子句在任何情况下都会执行。在TypeError通过将两个字符串凸起不被处理except子句和后因此再次加注finally条款已经被执行。

在现实世界的应用程序中,无论是否成功使用资源,finally子句对于释放外部资源(例如文件或网络连接)很有用。


nur*_*tin 5

多年来专业使用 delphi 教会了我如何使用finally来保护我的清理例程。Delphi 几乎强制使用finally 来清理try 块之前创建的所有资源,以免导致内存泄漏。这也是 Java、Python 和 Ruby 的工作原理。

resource = create_resource
try:
  use resource
finally:
  resource.cleanup
Run Code Online (Sandbox Code Playgroud)

无论您在尝试和最终之间做什么,资源都会被清理。此外,如果执行从未到达该块,它也不会被清理try。(即create_resource本身抛出异常)它使您的代码“异常安全”。

至于为什么你实际上需要一个finally块,并不是所有语言都需要。在 C++ 中,您会自动调用析构函数,当异常展开堆栈时,析构函数会强制进行清理。我认为与 try...finally 语言相比,这是朝着更清晰的代码方向迈出的一步。

{    
  type object1;
  smart_pointer<type> object1(new type());
} // destructors are automagically called here in LIFO order so no finally required.
Run Code Online (Sandbox Code Playgroud)