一、开篇:try的四件套
try语句有四个子句:try、except、else、finally。大多数人只用到了前两个,但else和finally各有独特而重要的用途。
⌨️ 完整结构:
# try-except-else-finally 完整结构try:# 可能出错的代码result=perform_operation()exceptSomeError:# 处理已知的异常handle_error()else:# try块成功执行后运行(没有异常时)process_result(result)finally:# 无论如何都会运行(清理资源)cleanup()二、else:成功时才执行
2.1 else的作用
# else子句:在try块没有抛出异常时执行# 它的代码不在try的保护范围内——出了异常不会被except捕获defload_and_process(filepath):"""加载并处理数据——else的使用"""try:data=open(filepath,'r').read()exceptFileNotFoundError:print(f"文件不存在:{filepath}")returnNoneelse:# 文件读取成功——处理数据(不在try保护范围内)print("文件读取成功,开始处理...")processed=data.upper()returnprocessed# 💡 为什么不用在try块中继续写?# 把可能出错的代码(open)和正常处理的代码(upper)分开# 如果upper()出错了——这是处理逻辑的bug,不应该被FileNotFoundError的except捕获# 对比:# ❌ 不推荐:把处理逻辑也放在try中defload_and_process_bad(filepath):try:data=open(filepath,'r').read()processed=data.upper()# 如果这里出了异常呢?returnprocessedexceptFileNotFoundError:# 可能是文件不存在,也可能是upper()出的其他异常print(f"文件不存在:{filepath}")returnNone2.2 else的典型场景
# 场景一:数据库操作defquery_database(connection,query):try:cursor=connection.cursor()cursor.execute(query)exceptDatabaseErrorase:print(f"查询失败:{e}")return[]else:# 查询成功——获取结果results=cursor.fetchall()returnresults# 场景二:API调用deffetch_user_data(user_id):try:response=requests.get(f"/api/users/{user_id}",timeout=5)response.raise_for_status()# HTTP错误会抛异常exceptrequests.RequestExceptionase:print(f"请求失败:{e}")returnNoneelse:# 请求成功——解析数据returnresponse.json()# 场景三:输入验证defget_positive_number(prompt):try:value=int(input(prompt))exceptValueError:print("请输入有效的整数")returnNoneelse:# 类型转换成功——验证值的范围ifvalue<=0:print("请输入正数")returnNonereturnvalue三、finally:无论如何都执行
3.1 finally的核心规则
# finally:无论是否发生异常、异常是否被捕获,都会执行# 主要用于清理资源:关闭文件、释放锁、断开连接# 演示——多种情况deftest_finally(scenario):"""演示finally在各种情况下的执行"""try:print(" try开始")ifscenario=="error":raiseValueError("测试异常")ifscenario=="success":print(" try正常完成")returnf"结果:{scenario}"exceptValueError:print(" except捕获了异常")return"异常结果"finally:print(" finally执行了!")# 注意:finally中的代码即使在try/except中有return也会执行!print("场景1: 正常")result1=test_finally("success")print(f" 返回值:{result1}\n")print("场景2: 异常")result2=test_finally("error")print(f" 返回值:{result2}\n")# ⚠️ finally中的return会覆盖之前的return!defbad_finally():try:return"try中的return"finally:return"finally中的return"# 这会覆盖try的return!print(bad_finally())# "finally中的return" —— 不推荐!3.2 finally的资源清理
# ⌨️ finally最常见的用途——确保资源被释放importthreading# 场景一:文件操作defread_file_with_finally(filepath):f=Nonetry:f=open(filepath,'r',encoding='utf-8')returnf.read()exceptFileNotFoundError:print(f"文件不存在:{filepath}")returnNonefinally:iff:# 无论是否成功,都要尝试关闭文件f.close()print("文件已关闭")# 场景二:锁的释放classThreadSafeCounter:def__init__(self):self._lock=threading.Lock()self._value=0defincrement(self):self._lock.acquire()try:self._value+=1returnself._valuefinally:self._lock.release()# 必须释放!即使操作失败# 场景三:临时文件清理importosdefprocess_with_temp_file(data):temp_path="temp_processing.txt"try:withopen(temp_path,'w')asf:f.write(data)# 处理临时文件...returnos.path.getsize(temp_path)finally:ifos.path.exists(temp_path):os.remove(temp_path)print("临时文件已清理")四、try-except-else-finally组合实战
4.1 完整的数据库事务
deftransfer_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,))ifcursor.fetchone()[0]<0:raiseValueError("余额不足")exceptValueErrorase:print(f"业务错误:{e}")conn.rollback()returnFalseexceptExceptionase:print(f"数据库错误:{e}")conn.rollback()returnFalseelse:# 执行成功conn.commit()print("转账成功")returnTruefinally:cursor.close()4.2 计时和日志
importtimeimportloggingdeftimed_operation(operation_name):"""装饰器——记录操作耗时(无论成功与否)"""defdecorator(func):defwrapper(*args,**kwargs):start=time.perf_counter()try:result=func(*args,**kwargs)exceptExceptionase:elapsed=time.perf_counter()-start logging.error(f"{operation_name}失败 "f"(耗时{elapsed:.3f}秒):{e}")raiseelse:elapsed=time.perf_counter()-start logging.info(f"{operation_name}成功 "f"(耗时{elapsed:.3f}秒)")returnresultfinally:# 无论如何都记录passreturnwrapperreturndecorator@timed_operation("数据处理")defprocess_large_dataset(data):# ... 处理逻辑 ...time.sleep(0.5)returnlen(data)result=process_large_dataset([1,2,3])五、总结
try-except-else-finally是Python异常处理的完整工具。else让成功路径清晰,finally让资源清理万无一失。
💡核心要点:
| 子句 | 何时执行 | 主要用途 |
|---|---|---|
try | 总是首先执行 | 可能出错的代码 |
except | try中发生异常时 | 处理已知异常 |
else | try中没有异常时 | 处理"成功"的结果 |
finally | 无论如何都执行 | 清理资源(关闭文件、释放锁) |
✅最佳实践:with语句可以替代大多数finally场景(文件、锁等)。只有在with不够用时才用finally。