当前位置:首页>python>Python异常处理:别再用裸 except 了

Python异常处理:别再用裸 except 了

  • 2026-10-11 08:11:26
Python异常处理:别再用裸 except 了

周五晚上的噩梦

去年周五晚上上线,一个小模块报错。我图省事写了except:,以为万事大吉。结果静默吞掉了数据库连接异常,数据丢了半小时才发现。我在公司待到凌晨三点。😂

# 我的作死代码
defsave_data(data):
try:
        db.insert(data)
except:           # 千万别这么干
pass

裸except是黑洞

except:捕获什么?KeyboardInterrupt、SystemExit、GeneratorExit,全吞。Ctrl+C退不出,用户以为死机。不是异常处理,是黑洞。

# 捕获范围层级
BaseException
  ├── SystemExit          <- 不该抓
  ├── KeyboardInterrupt   <- 不该抓
  ├── GeneratorExit       <- 不该抓
  └── Exception           <- 抓这个

具体异常是底线

说点得罪人的:写except Exception都比你写裸except强。但最好再具体点。try/except是为了处理预期内的错误,不是给代码打麻醉。

import requests

deffetch(url):
try:
        resp = requests.get(url, timeout=5)
        resp.raise_for_status()
return resp.json()
except requests.exceptions.Timeout:
        print("超时,换备用URL")
return fetch_backup(url)
except requests.exceptions.HTTPError as e:
        print(f"服务器抽风: {e}")
returnNone

else放主逻辑

很多人只用try/except,把else和finally当空气。else是try没异常时才执行,放主逻辑;finally无论咋样都执行,放清理代码。分工明确。

defread_config(path):
    f = None
try:
        f = open(path, 'r')
except FileNotFoundError:
        print("配置丢了,用默认的")
return {}
else:
import json
return json.load(f)
finally:
if f:
            f.close()

with语句更偷懒

上面那段可以更懒。with自动调finally,语义化更强。文件、锁、连接,我现在无脑with。代码少一半,安心多一倍。

defread_config(path):
try:
with open(path, 'r') as f:
import json
return json.load(f)
except (FileNotFoundError, json.JSONDecodeError) as e:
        print(f"读取失败: {e}")
return {}

别再用字符串raise

我见过最离谱的:raise一个字符串。raise "出错了"。Python2里居然能跑,Python3终于禁了。自定义异常类才是正路。

classBizError(Exception):
"""业务异常基类"""
pass

classInsufficientBalance(BizError):
def__init__(self, uid, amount, balance):
        self.uid = uid
        self.amount = amount
        self.balance = balance
        super().__init__(
f"用户{uid}余额不足,"
f"需要{amount},只有{balance}"
        )

defdeduct(uid, amount):
    balance = get_balance(uid)
if balance < amount:
raise InsufficientBalance(uid, amount, balance)
    do_deduct(uid, amount)

异常也能批量抓

有时多个异常同一种处理逻辑,可以用元组批量捕获。代码清爽,不会漏。这比写一堆重复的except块优雅多了。

try:
    risky_operation()
except (ConnectionError, TimeoutError, SSLError):
    logger.warning("网络相关错误,准备重试")
    retry()

异常链保留现场

封装底层异常时,原始堆栈丢了最痛。raise from把异常链串起来,追查问题一目了然。别让你的异常变成无头案。

defparse_user(data):
try:
return json.loads(data)
except json.JSONDecodeError as e:
raise ValueError(
f"用户数据解析失败: {data[:50]}..."
        ) from e

# traceback 显示两条链:
# 1. json.JSONDecodeError
# 2. ValueError 用户数据解析失败...

异常不是goto

我见过把异常当流程控制用的。循环里抛异常跳出,判断分支用raise。求你了,异常是处理异常情况的。性能差,语义烂。

# 烂代码
values = [1,2,3]
try:
for v in values:
if v > 5:
raise StopIteration
except StopIteration:
    result = v

# 正常人
result = next((v for v in values if v > 5), None)

日志别用print

异常捕获了不记录,等于没捕获。用logging,级别选对:预期内用warning,意料外用error,带着traceback一起记。

import logging
logger = logging.getLogger(__name__)

defdivide(a, b):
try:
return a / b
except ZeroDivisionError:
        logger.error("除零错误", exc_info=True)
return float('inf')
except TypeError:
        logger.exception("参数类型不对: %s, %s", a, b)
returnNone

我的四连问

现在写代码我会扫一眼:具体异常类型写了吗?else/finally用了吗?异常链保留了吗?会不会静默吞错误?这几问下来,能避开八成坑。

+----------------+------------------------+
| 反模式         | 正确做法               |
+----------------+------------------------+
| except:        | except SpecificError:  |
| 裸pass         | log + 处理/重抛        |
| raise "字符串" | raise CustomError(msg) |
| 吞掉所有异常   | 只捕获能处理的         |
| 异常当if用     | 正常流程控制           |
+----------------+------------------------+

最新文章

随机文章