finally在Python中总是执行吗?

qnakjoqk  于 2023-01-12  发布在  Python
关注(0)|答案(6)|浏览(247)

对于Python中任何可能的try-finally块,是否保证finally块总是被执行?
例如,假设我在except块中返回:

try:
    1/0
except ZeroDivisionError:
    return
finally:
    print("Does this code run?")

或者,我可能会重新引发Exception

try:
    1/0
except ZeroDivisionError:
    raise
finally:
    print("What about this code?")

测试表明,finally确实在上述示例中得到了执行,但我想还有其他我没有想到的场景。

是否存在finally块在Python中执行失败的情况?

6mzjoqzu

6mzjoqzu1#

是的。最后(几乎)总是胜利。
有一些方法可以在finally:有机会执行之前停止执行(例如,使解释器崩溃、关闭计算机、永久挂起生成器)。
我想还有其他我没想到的情况。
下面还有一些你可能没有想到的:

def foo():
    # finally always wins
    try:
        return 1
    finally:
        return 2
    
def bar():
    # even if he has to eat an unhandled exception, finally wins
    try:
        raise Exception('boom')
    finally:
        return 'no boom'

根据您退出解释器的方式,有时您可以最终“取消”,但不是像这样:

>>> import sys
>>> try:
...     sys.exit()
... finally:
...     print('finally wins!')
... 
finally wins!
$

使用不稳定的os._exit(在我看来,这福尔斯“崩溃解释器”):

>>> import os
>>> try:
...     os._exit(1)
... finally:
...     print('finally!')
... 
$

我目前正在运行这段代码,以测试最终是否仍将执行后,热寂的宇宙:

try:
    while True:
       sleep(1)
finally:
    print('done')

不过,我仍在等待结果,所以请稍后再回来查看。

qij5mzcb

qij5mzcb2#

根据Python documentation
无论之前发生了什么,final-block都会在代码块完成并处理了任何引发的异常后执行,即使异常处理器或else-block中出现错误并引发了新的异常,final-block中的代码仍然会运行。
还应该注意的是,如果有多个return语句,包括finally块中的一个,那么finally块的return语句将是唯一执行的语句。

rwqw0loc

rwqw0loc3#

嗯,是也不是。
可以保证的是,Python总是会尝试执行finally块,如果你从finally块返回或者引发一个未捕获的异常,finally块会在实际返回或者引发异常之前执行。
(what您可以通过简单地运行问题中的代码来控制自己)
我能想象到的唯一一种情况是,当Python解释器本身崩溃时,例如在C代码内部或由于断电,finally块不会被执行。

llmtgqce

llmtgqce4#

我没有使用生成器函数就找到了这个:

import multiprocessing
import time

def fun(arg):
  try:
    print("tried " + str(arg))
    time.sleep(arg)
  finally:
    print("finally cleaned up " + str(arg))
  return foo

list = [1, 2, 3]
multiprocessing.Pool().map(fun, list)

休眠可以是任何可能运行不一致时间量的代码。
这里发生的事情是,第一个并行进程成功地离开了try块,但随后试图从函数返回一个没有在任何地方定义的值(foo),这导致了一个异常,这个异常在其他进程到达finally块之前就杀死了map。
同样,如果你在try块中的sleep()调用之后添加bar = bazz行,那么第一个到达该行的进程会抛出一个异常(因为bazz没有定义),这会导致它自己的finally块运行,但随后会杀死map,导致其他try块在没有到达finally块的情况下消失,第一个进程也不会到达它的return语句。
对于Python多处理来说,这意味着即使有一个进程出现异常,也不能相信异常处理机制能够清理所有进程中的资源,额外的信号处理或管理多处理Map调用之外的资源是必要的。

xdyibdwo

xdyibdwo5#

您可以将finally与if语句一起使用,下面的示例检查网络连接,如果已连接,则将运行finally块

try:

                reader1, writer1 = loop.run_until_complete(self.init_socket(loop))

                x = 'connected'

            except:

                print("cant connect server transfer") #open popup

                x = 'failed'

            finally  :
                
                if x == 'connected':

                    with open('text_file1.txt', "r") as f:

                        file_lines = eval(str(f.read()))

                else:
                     print("not connected")
sshcrbum

sshcrbum6#

“保证”这个词比finally的任何实现都要有力得多。保证的是,如果执行流出整个try-finally结构,它将通过finally这样做。不保证的是,执行将流出try-finally

def gen(text):
    try:
        for line in text:
            try:
                yield int(line)
            except:
                # Ignore blank lines - but catch too much!
                pass
    finally:
        print('Doing important cleanup')

text = ['1', '', '2', '', '3']

if any(n > 1 for n in gen(text)):
    print('Found a number')

print('Oops, no cleanup.')

请注意,这个例子有点棘手:当生成器被垃圾回收时,Python试图通过抛出GeneratorExit异常来运行finally块,但是在这里我们捕获了那个异常,然后又捕获了yield,此时Python打印了一个警告(“generator ignored GeneratorExit”)并放弃了。
生成器或协程可能不会执行到最后的其他情况包括对象从未被GC'艾德(是的,这是可能的,即使在CPython中也是如此),或者async withawait__aexit__中,或者对象awaityieldfinally块中。

由于multiprocessing在使用 fork start方法(Unix上的默认方法)时使用fork-without-exec创建工作进程,然后在工作进程完成后调用工作进程中的os._exit,因此finallymultiprocessing的交互可能会有问题(example)。

  • C级分段故障将阻止finally块运行。
  • kill -SIGKILL将阻止finally块运行。SIGTERMSIGHUP也将阻止finally块运行,除非您自己安装一个处理程序来控制关闭;默认情况下,Python不处理SIGTERMSIGHUP
  • finally中的一个异常可能会阻止清理工作的完成,一个特别值得注意的情况是,如果用户在我们开始执行finally块时点击了control-C *,Python将引发一个KeyboardInterrupt并跳过finally块的每一行内容(KeyboardInterrupt-安全代码很难写)。
  • 如果计算机断电,或者计算机休眠并且没有唤醒,finally块将不会运行。

finally块不是事务系统;它不提供原子性保证或任何类似的东西,其中一些例子可能看起来很明显,但很容易忘记这样的事情可能会发生,并过于依赖finally

相关问题