Python技术迷

Python 的try、 except和 finally:异常处理

这不是一句玩笑,是我干活这些年来,最深的感悟之一。比如,有个用户填生日的时候居然写“1792 年”,我一看就觉得这是想薅我们系统羊毛呢,毕竟连拿破仑都还没当皇帝,咱代码已经得去判断人家是不是活化石了。

而要应对这些神操作,我们就得靠 Python 的异常处理机制:try-except-finally。

谁能想到

我以前刚入行的时候,写了这么一段代码:

with open("config.yaml") as f:
    config = f.read()

理想中,它应该乖乖打开文件,把配置读出来。但现实是,文件根本就没这个名字!然后程序啪的一下挂掉了,用户一脸懵逼,说“怎么连点按钮都不让我点了?”

这时候,你就会明白:try-except不是锦上添花,是救命稻草。

基础款 try-except

Python 的基本异常处理结构就像这样:

try:
# 有可能出错的代码
    risky_operation()
except SomeError:
# 出错后的反应
    handle_error()
finally:
# 不管出没出错,都执行
    cleanup()

举个接地气的例子,我们打开一个配置文件,如果用户鬼使神差删了这个文件,那我们要么提醒他,要么用默认配置:

try:
with open("user_config.yaml") as f:
        config = yaml.safe_load(f)
except FileNotFoundError:
    print("找不到配置文件,用默认的吧。")
    config = get_default_config()
except IsADirectoryError:
    print("哥们你给我一个文件夹让我打开是想让我上树吗?")
except yaml.YAMLError as e:
    print(f"配置文件格式不对:{e}")
finally:
    print("配置文件加载流程结束。")

这段代码的好处是,出了问题程序也不会崩,我们还有机会拉它一把。

生产环境中的“那一套”

写给自己玩的代码你可以 print("出错了") 就拉倒,但一到线上环境,这么搞分分钟让你成为运维眼中的“幽灵程序员”。

日志记录是刚需

日志是排查问题的望远镜,没日志你就是个睁眼瞎。标准做法是用 logging 模块:

import logging

logging.basicConfig(level=logging.INFO)

try:
    do_something()
except Exception as e:
    logging.exception("操作失败了!")

这个 logging.exception() 会自动把异常堆栈信息全打印出来,你以后翻日志也不至于抓耳挠腮。

要不要重试?要看场合

比如请求一个 API 接口,如果失败了你立刻放弃,那你可能错失一次高可用的机会。可以用 time.sleep() + for 循环搞个简易重试机制:

import time

for i in range(3):
try:
        response = requests.get("http://example.com/api")
break
except requests.ConnectionError:
        print(f"第 {i+1} 次连接失败,1秒后重试...")
        time.sleep(1)
else:
    print("连续三次失败,我尽力了。")

当然,生产代码建议用更强大的重试库,比如 tenacity。

finally 的隐藏用法

很多人只知道 finally 会“总是执行”,但没理解它有多重要。比如你开了个数据库连接,不管中途出不出错,你都得关掉它吧?

conn = open_connection()
try:
    do_something_with(conn)
except Exception as e:
    logging.error(f"操作出错:{e}")
finally:
    conn.close()

这就像打完仗再脏也得擦枪一样,有始有终。

一个小插曲:我当年犯过的错

当年我写了段代码,一次 except 里什么都没写,默默吞下异常:

try:
    do_important_thing()
except:
pass

结果系统里一个关键流程一直没执行,直到有天被领导发现数据对不上,我差点就下岗。后来学聪明了,最起码也得加个日志,别什么都吃。

面试题:Python 中 try、except、finally 的执行顺序是怎样的?

最优答案:

try 语句用于捕获和处理异常,基本结构是:

try:
# 可能引发异常的代码
except SomeException:
# 异常处理逻辑
finally:
# 无论是否发生异常,都会执行

执行顺序如下:

  1. try 中代码首先执行;
  2. 如果发生异常,立即跳转到第一个匹配的 except;
  3. 无论是否发生异常,finally 都会执行;
  4. 如果 try 和 except 中有 return,finally 仍然会在返回前执行;
  5. 如果 finally 中也有 return,它会覆盖前面的 return。

举个例子:

deftest():
try:
return1
finally:
return2

print(test())  # 输出 2

所以说,finally 不是陪跑,是最后的发言权。

总之,我觉得在 Python 开发里,异常处理就是一种“防蠢”机制——防用户蠢、防自己蠢、防世界太离谱。合理用好 try-except-finally,你就能让程序活得更久,维护的人更幸福,出错时也不至于鸡飞狗跳。