Python异常处理else和finally清理资源实战
在 Python 中,try 异常处理结构并不只有 try 和 except,完整形式包括 try、except、else、finally 四个子句。多数脚本只使用前两个,但 else 能把成功路径从 try 中分离出来,finally 能保证资源清理一定执行。下面按执行顺序、代码实现和典型场景说明。一、完整结构与执行顺序
try:
result = perform_operation()
except SomeError:
handle_error()
else:
process_result(result)
finally:
cleanup()
try 总是先执行;except 只在 try 抛出匹配异常时执行;else 只在 try 无异常时执行;finally 无论是否异常、异常是否被捕获都执行。这个顺序决定了 else 和 finally 的用途。
二、else:把成功路径放到 try 外
else 的代码不在 try 保护范围内,因此它抛出的异常不会被前面的 except 捕获。推荐把可能出错的 open 和正常处理逻辑 upper 分开,避免处理逻辑 bug 被 FileNotFoundError 错误捕获。
def load_and_process(filepath):
try:
data = open(filepath, 'r').read()
except FileNotFoundError:
print(f'文件不存在: {filepath}')
return None
else:
print('文件读取成功,开始处理...')
processed = data.upper()
return processed
对比把处理逻辑一并放入 try 的写法:
def load_and_process_bad(filepath):
try:
data = open(filepath, 'r').read()
processed = data.upper()
return processed
except FileNotFoundError:
print(f'文件不存在: {filepath}')
return None
这种方式会把 upper 等处理逻辑的异常也归入 FileNotFoundError 的处理语境,不利于定位问题。
典型场景一:数据库查询
def query_database(connection, query):
try:
cursor = connection.cursor()
cursor.execute(query)
except DatabaseError as e:
print(f'查询失败: {e}')
return []
else:
results = cursor.fetchall()
return results
典型场景二:API 调用
def fetch_user_data(user_id):
try:
response = requests.get(f'/api/users/{user_id}', timeout=5)
response.raise_for_status()
except requests.RequestException as e:
print(f'请求失败: {e}')
return None
else:
return response.json()
典型场景三:输入验证
def get_positive_number(prompt):
try:
value = int(input(prompt))
except ValueError:
print('请输入有效的整数')
return None
else:
if value <= 0:
print('请输入正数')
return None
return value
三、finally:无论如何都执行
finally 的核心规则是:无论是否发生异常、异常是否被捕获,都会执行;即使 try 或 except 中有 return,finally 也会执行。注意 finally 中写 return 会覆盖之前的 return,不建议这样用。
def test_finally(scenario):
try:
print('try开始')
if scenario == 'error':
raise ValueError('测试异常')
if scenario == 'success':
print('try正常完成')
return f'结果: {scenario}'
except ValueError:
print('except捕获了异常')
return '异常结果'
finally:
print('finally执行了!')
print('场景1: 正常')
result1 = test_finally('success')
print(f'返回值: {result1}')
print('场景2: 异常')
result2 = test_finally('error')
print(f'返回值: {result2}')
def bad_finally():
try:
return 'try中的return'
finally:
return 'finally中的return'
print(bad_finally())
bad_finally 会输出 finally中的return,说明 finally 的 return 覆盖了 try 的 return。
资源清理是 finally 最常见的用途。文件操作示例:
def read_file_with_finally(filepath):
f = None
try:
f = open(filepath, 'r', encoding='utf-8')
return f.read()
except FileNotFoundError:
print(f'文件不存在: {filepath}')
return None
finally:
if f:
f.close()
print('文件已关闭')
线程锁释放示例:
import threading
class ThreadSafeCounter:
def __init__(self):
self._lock = threading.Lock()
self._value = 0
def increment(self):
self._lock.acquire()
try:
self._value += 1
return self._value
finally:
self._lock.release()
临时文件清理示例:
import os
def process_with_temp_file(data):
temp_path = 'temp_processing.txt'
try:
with open(temp_path, 'w') as f:
f.write(data)
return os.path.getsize(temp_path)
finally:
if os.path.exists(temp_path):
os.remove(temp_path)
print('临时文件已清理')
四、try-except-else-finally 组合实战
数据库事务是典型组合场景:try 中执行多条 SQL,异常时 rollback,成功时在 else 中 commit,finally 关闭游标。
def transfer_money(conn, from_id, to_id, amount):
try:
cursor = conn.cursor()
cursor.execute('BEGIN TRANSACTION')
cursor.execute(
'UPDATE accounts SET balance = balance - ? WHERE id = ?',
(amount, from_id)
)
cursor.execute(
'UPDATE accounts SET balance = balance + ? WHERE id = ?',
(amount, to_id)
)
cursor.execute('SELECT balance FROM accounts WHERE id = ?', (from_id,))
if cursor.fetchone() < 0:
raise ValueError('余额不足')
except ValueError as e:
print(f'业务错误: {e}')
conn.rollback()
return False
except Exception as e:
print(f'数据库错误: {e}')
conn.rollback()
return False
else:
conn.commit()
print('转账成功')
return True
finally:
cursor.close()
这里 else 只在所有 SQL 执行且余额检查通过后提交事务;任何异常进入 except 回滚;finally 关闭游标。
计时和日志装饰器也能体现完整结构:except 中记录失败耗时并重新抛出,else 中记录成功耗时并返回结果,finally 可用于统一收尾。
import time
import logging
def timed_operation(operation_name):
def decorator(func):
def wrapper(*args, **kwargs):
start = time.perf_counter()
try:
result = func(*args, **kwargs)
except Exception as e:
elapsed = time.perf_counter() - start
logging.error(
f'{operation_name} 失败 '
f'(耗时{elapsed:.3f}秒): {e}'
)
raise
else:
elapsed = time.perf_counter() - start
logging.info(
f'{operation_name} 成功 '
f'(耗时{elapsed:.3f}秒)'
)
return result
finally:
pass
return wrapper
return decorator
@timed_operation('数据处理')
def process_large_dataset(data):
time.sleep(0.5)
return len(data)
result = process_large_dataset()
五、总结与选型
在 Python 异常处理中,try-except-else-finally 是完整工具。else 让成功路径清晰,finally 让资源清理可靠。各子句作用可归纳为:try 总是首先执行,放可能出错的代码;except 在 try 中发生异常时执行,处理已知异常;else 在 try 中没有异常时执行,处理成功结果;finally 无论如何都执行,清理资源,如关闭文件、释放锁等。
最佳实践是:with 语句可以替代大多数 finally 场景,例如文件、锁等;只有在 with 不够用时才手写 finally。
Re: Python异常处理else和finally清理资源实战
这篇把 try/except/else/finally 的执行顺序讲得很清楚,else 那块尤其有用:把可能出错的读取和成功后的处理分开,能避免 upper 之类的处理异常被 FileNotFoundError 误捕获。finally 里 return 会覆盖 try/except 的 return 这个细节也很值得记。最后文件操作的例子好像没发完,停在 retur 了,方便的话补一下资源清理的完整写法,正好想看看 f 为空时 finally 里怎么处理。感谢分享。Re: Python异常处理else和finally清理资源实战
感谢分享,写得很清楚。else 和 finally 确实很容易被忽略,尤其是 else 不在 try 保护范围内这一点,把 open 和后续处理拆开,能避免业务逻辑异常被 FileNotFoundError 误捕获,这个实战价值很高。数据库、API、输入验证那几个例子也把成功路径单独拎出来了,思路很清晰。finally 部分讲得也到位,无论是否异常都要执行的特性很适合资源清理,同时提醒 finally 里 return 会覆盖前面 return,这个坑确实要注意。文件清理那段好像断在 retur 了,期待补完整,特别是 finally 关闭文件和异常传播的完整流程。Re: Python异常处理else和finally清理资源实战
写得挺清楚的,之前确实习惯只用 try 和 except,else 和 finally 经常被忽略。把成功路径从 try 里拆出来这点很有启发,尤其是文件读取和数据处理分开,避免把 upper 之类的处理 bug 也当成 FileNotFoundError,这个例子很直观。 finally 即使遇到 return 也会执行,以及 finally 里写 return 会覆盖前面的 return,这个坑确实容易出隐蔽问题,提醒得很到位。最后 read_file_with_finally 那段好像帖子截断了,期待补全文件关闭和异常处理的部分。整体很适合脚本实战,感谢分享。
页:
[1]