一名 PHP 工程师的 Python 系统学习笔记(05):异常处理与错误设计
真正拉开 Python 代码质量差距的,往往不是业务复杂度,而是你怎么处理错误。
很多人初学 Python,对异常的理解停留在:
try: ...except Exception:pass
代码不报错了,但问题也被一起吞掉了。
这一篇只讲一件事:在工程里,Python 的异常到底该怎么用。
一、异常不是“失败”,而是一种控制流
在 Python 中,异常是被设计成常态机制的。
defdiv(a, b):return a / b
div(10, 0) # ZeroDivisionError
👉 Python 的选择很明确:
与其返回错误码,不如直接打断执行。
这和 PHP 早期“返回 false”的风格,有本质区别。
二、不要用 Exception 当“万能兜底”
try: risky()except Exception: handle()
这在工程里是高危写法。
更合理的方式是:
try: risky()except ValueError: handle_value()except IOError: handle_io()
👉 你捕获的异常,应该是你“预期会发生的”。
三、异常应该“离问题近,离入口远”
一个常见反模式:
defread_conf():try:return open("conf.txt").read()except Exception:returnNone
这样做的问题是:
更好的方式:
defread_conf():return open("conf.txt").read()
在更高层统一处理异常。
👉 原则一句话:
异常要在“知道如何处理”的地方捕获。
四、自定义异常,比你想象中重要
classConfigError(Exception):pass
ifnot conf:raise ConfigError("config not found")
工程意义只有一个:
👉 用异常类型,表达业务语义。
这比返回字符串、错误码都清晰得多。
五、finally 的唯一职责
try: f = open("a.txt") work(f)finally: f.close()
finally 只做一件事:
资源回收。
不要在 finally 里写业务逻辑。
六、异常 vs 返回值:工程选择
👉 不要混用,否则调用方会非常痛苦。
七、这一篇你只需要记住 5 件事
八、完整示例
import jsondefload_config(path: str) -> dict:""" 读取并解析配置文件 """with open(path, "r", encoding="utf-8") as f:try:return json.load(f)except json.JSONDecodeError as e:raise ConfigError("配置文件格式错误") from eclassConfigError(Exception):"""配置相关错误"""passclassDBConnectionError(Exception):"""数据库连接错误"""passdefconnect_db(conf: dict):""" 模拟数据库连接 """ print(f"正在连接数据库:{conf}")if"host"notin conf:raise DBConnectionError("缺少数据库 host 配置")if conf.get("host") == "bad_host":raise DBConnectionError("数据库连接失败") print("数据库连接成功")return"db_connection"defmain():try: conf = load_config("config/day5_config.json") db = connect_db(conf) print(f"系统启动完成: {db}")except ConfigError as e: print(f"[配置错误] {e}")except DBConnectionError as e: print(f"[数据库错误] {e}")except Exception as e:# 真正的兜底:记录 + 报警 print(f"[未知错误] {e}")main()
写在最后
很多 Python 项目,后期变得难维护,
问题往往不是功能复杂,而是:
错误被悄悄吃掉了。
下一篇我会继续写一个和异常高度相关、但经常被忽略的话题:
上下文管理器 with:如何优雅地管理资源
如果你也是从 PHP / Go 转 Python,希望这个系列能真正帮你建立工程级直觉。