finally 在 return 返回值暂存后、函数真正退出前强制执行,由字节码 SETUP_FINALLY 和 END_FINALLY 指令保障;若 finally 中有 return,则覆盖原返回值,且可能中断清理逻辑。

finally 不是“在 return 后执行”,而是在 return 的返回动作生效前强制插入执行——这是 Python 解释器字节码层的硬性保障,不是逻辑顺序上的“之后”。
finally 的执行时机由字节码指令强制控制
Python 编译器在生成字节码时,只要函数里有 try-finally,就会插入 SETUP_FINALLY 指令,并在对应栈帧退出前无条件触发 END_FINALLY。
这意味着:
-
return表达式求值完成后,返回值被暂存,但控制流不会立即跳出函数 - 解释器必须先跳转到
finally块执行全部语句 - 执行完
finally后,才真正把暂存的返回值交出去(除非finally自己return)
常见误判场景:
- 看到
return "done"就以为函数立刻结束 → 实际上它只是“准备返回”,还没走完 - 认为
finally是靠“判断是否该执行”来兜底 → 它根本没判断,是栈帧销毁时自动触发的 cleanup 钩子
finally 中写 return 会覆盖原返回值
这是设计使然,不是 bug。一旦 finally 里出现 return,它就劫持整个函数出口:
-
try里的return被忽略 -
except里的return同样失效 - 异常也会被吞掉(原异常丢失,只返回
finally的值)
示例:
立即学习“Python免费学习笔记(深入)”;
def f():
try:
return "from try"
finally:
return "from finally"
调用 f() 返回的是 "from finally",不是 "from try"。
注意:
- 这种覆盖对资源清理逻辑很危险:比如你本想关文件,却意外提前
return,导致后续清理代码没跑完 - 若需确保清理完成再返回,
finally内应避免return、raise或其他中断语句
finally 真正不执行的两种情况
几乎所有“没执行”的抱怨,其实都源于没分清「没进入 try」和「没执行 finally」:
-
try块根本没开始执行:比如模块导入失败、语法错误、或try前就sys.exit() - 外部强制终止:
kill -9、断电、内核 panic —— 此时连 Python 解释器都没机会响应
只要控制流进入了 try 的第一行,finally 就已注册为 cleanup 钩子,后续无论怎么跳(return、break、sys.exit(0)、未捕获异常),它都会被执行。
真正容易被忽略的点是:清理逻辑本身是否允许被中断。比如在 finally 里调用一个可能抛异常的关闭方法,又没做防护,就会吞掉原始错误——这不是 finally 不可靠,而是它太可靠,可靠到连你的疏忽都照单全收。


















