3、else和finally子句:else的用法、finally的用法、资源清理最佳实践

异常处理这块,很多人只盯着try和except。其实else和finally才是真正体现功力的地方。我刚开始写Python那会儿,也经常把else和finally搞混,后来踩了几个坑才彻底搞明白。

else子句:没异常时才执行

else的用法其实很简单——当try块没有抛出任何异常时,才会执行else里的代码。说白了,它就是「一切正常」的分支。

核心逻辑:try成功 → 执行else → 跳过except

看个例子:

def read_config(file_path):
    try:
        with open(file_path, 'r') as f:
            data = f.read()
    except FileNotFoundError:
        print("配置文件不存在,使用默认配置")
        return {}
    else:
        # 只有文件读取成功才会执行
        print("配置文件读取成功")
        return json.loads(data)

我在项目中遇到过这样的场景:有人把本该放在else里的代码直接塞进了try块。结果呢?一旦那段代码本身也抛异常,排查起来特别痛苦。你想想看,try块里代码越多,越难定位到底是哪一行出了问题。

我的习惯:try块只放可能抛出异常的代码,后续处理逻辑放else里。这样代码职责清晰,调试也方便。

finally子句:无论如何都会执行

finally就更有意思了。不管try里有没有异常,不管有没有return,finally一定会执行。嗯,这里要注意——哪怕你在try里写了return,finally也会在返回之前先跑一遍。

为什么会这样?因为Python解释器在执行return之前,会先检查有没有finally块。如果有,先执行finally,再真正返回。

def demo():
    try:
        print("try执行")
        return "返回值"
    finally:
        print("finally执行")

result = demo()
print(result)
# 输出:
# try执行
# finally执行
# 返回值

我曾经踩过一个坑:在finally里写了return语句,结果把try里的返回值给覆盖了。那会儿排查了半天,最后发现是finally里的return搞的鬼。所以我的建议是——finally里尽量不要写return,除非你明确知道自己在做什么。

避坑指南:我曾经在finally里修改了全局变量,导致异常发生时状态不一致。后来我改成只在finally里做清理工作,再也不碰业务逻辑了。

资源清理最佳实践

说到资源清理,finally就是最佳拍档。文件句柄、网络连接、数据库游标——这些资源用完必须释放,否则会出大问题。

我整理了几种常见的资源清理模式:

资源类型 清理方式 推荐做法
文件 close() 使用with语句
数据库连接 close() / rollback() try-finally或上下文管理器
网络socket close() try-finally
锁 release() try-finally

看一个数据库操作的完整示例:

import sqlite3

def query_user(user_id):
    conn = None
    try:
        conn = sqlite3.connect("users.db")
        cursor = conn.cursor()
        cursor.execute("SELECT * FROM users WHERE id = ?", (user_id,))
        result = cursor.fetchone()
    except sqlite3.DatabaseError as e:
        print(f"数据库查询失败:{e}")
        return None
    else:
        # 查询成功,返回结果
        return result
    finally:
        # 不管成功还是失败,都要关闭连接
        if conn:
            conn.close()
            print("数据库连接已关闭")

你可能会问:既然with语句这么好用,为什么还要用finally?其实两者不冲突。with语句本质上是上下文管理器,它内部已经实现了try-finally。但对于那些不支持with的资源,或者需要更细粒度控制的场景,finally就是最佳选择。

我的建议:能用with就用with,代码更简洁。需要自定义清理逻辑时,再用try-finally。别为了炫技而滥用finally。

三者配合:完整的异常处理模式

把try、except、else、finally组合起来,就形成了完整的异常处理模式:

try:
    # 可能出错的代码
    risky_operation()
except SomeError as e:
    # 处理特定异常
    handle_error(e)
except Exception as e:
    # 兜底处理
    log_error(e)
    raise
else:
    # 没异常时执行
    process_result()
finally:
    # 无论如何都要执行
    cleanup_resources()

这个模式我在实际项目中用了无数次。它把「尝试」、「处理异常」、「正常流程」、「清理资源」四个阶段分得清清楚楚。说白了,这就是异常处理的「四步走」策略。

下面这张图展示了完整的执行流程:

try-except-else-finally 执行流程 try 块 有异常? 是 except 块 否 else 块 finally 块 结束

看到没?无论走左边还是右边,最终都会汇聚到finally。这就是它「无论如何都会执行」的底气所在。

总结一下:

  • else:try成功才执行,别把正常逻辑塞进try里
  • finally:不管怎样都执行,资源清理的最佳场所
  • 三者配合:try-except-else-finally,各司其职,代码更清晰

最后说一句:异常处理不是为了处理异常而处理,而是为了让代码更健壮、更可维护。把else和finally用好,你的代码质量会上一个台阶。

可可精品资料库