finally 并非总执行,如 os._exit()、段错误或 finally 内未捕获异常时会被跳过;但对常规异常、return 等场景 guaranteed 执行,是资源清理的可靠位置。

finally 一定会执行吗?先看这个陷阱
不是所有情况 finally 都会执行——比如进程被 os._exit() 强制终止、发生段错误(SIGSEGV)、或在 finally 块里又抛出未捕获的异常并导致解释器退出时,它就跳过了。但对绝大多数常规异常(KeyboardInterrupt、ValueError、return 提前退出等),finally 确实保证运行。
关键判断:如果你要释放文件句柄、关闭网络连接、解锁线程锁、或调用 close()/shutdown() 类方法,finally 是比 except 更可靠的兜底位置。
文件操作中用 finally 关闭句柄的典型写法
别依赖 __del__ 或让 GC 回收,显式关闭才是可控的。下面这段代码看似合理,但有隐患:
f = open('data.txt')
try:
data = f.read()
# 处理数据...
except UnicodeDecodeError:
print("编码错误")
# 这里没关 f!如果上面 return/raise/崩溃,f 就泄露了
正确做法是把 open() 放在 try 外,close() 放进 finally:
f = open('data.txt')
try:
data = f.read()
if 'ERROR' in data:
raise ValueError('bad content')
except ValueError as e:
print(e)
finally:
f.close() # 即使 read() 抛异常、或中间 return,这里也执行
-
f必须在try外初始化,否则open()失败时根本进不了finally - 如果
f.close()自己可能抛异常(如磁盘只读导致 flush 失败),应再包一层try/except,避免掩盖原始异常 - 更推荐用
with语句——它底层就是靠__exit__+finally逻辑实现的
在函数 return 途中,finally 什么时候介入?
finally 在函数真正返回「值」之前执行,但不改变已确定的返回值。看这个例子:
def demo():
try:
return "from try"
finally:
print("in finally")
return "from finally" # 这个 return 会覆盖 try 中的返回值
调用 demo() 输出:
in finally 'from finally'
所以要注意:
- 不要在
finally里写return,除非你明确想劫持返回值 - 如果只是清理资源,别碰
return;清理完让它自然落回try或except的返回路径 - 若清理过程需反馈(比如记录关闭失败),用日志或设置状态变量,别用
return或raise
多层嵌套 finally 和异常压制的真实影响
当 finally 块里发生新异常,它会压制 try 或 except 中正在传播的异常(Python 3.8+ 会附带警告 Exception ignored in: ...):
try:
raise ValueError("original")
except ValueError:
print("caught")
raise KeyError("new in except")
finally:
raise RuntimeError("in finally") # 这个会直接冒泡,ValueError 和 KeyError 都被吞了
这种压制极难调试。应对方式:
- 在
finally中做清理时,所有可能出错的操作都用try/except包住,并只记录错误,不raise - 例如:关闭 socket 时捕获
OSError,用logging.warning记录,然后继续执行后续清理 - 复杂资源管理建议优先用上下文管理器(
contextlib.contextmanager或自定义__enter__/__exit__),它们对异常传播更健壮
最常被忽略的一点:finally 不是“万能保险”,它只解决“执行时机”问题,不解决“执行安全”问题——清理动作本身是否可靠,得靠你额外兜底。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











