# 修客户的 Python 代码前，先找出吞掉错误的 except

> Brett Slatkin 第三版共 125 条，更新到 Python 3.13，适合当作接手代码的检查清单，不必从头读到尾。

原文: https://fdetimes.net/zh/books-courses/effective-python-third-edition-reading-order/

设想你到客户那里的第一周。对方的数据管道偶尔会写入空行，没人知道原因。你打开代码，发现一个调用 API 的函数被包在 `except Exception:` 里，一出问题就返回 `None`，也没有任何日志记录发生了什么。

这两个习惯在 Brett Slatkin 所著《Effective Python：编写高质量 Python 代码的 125 个有效方法》第三版中各有专门的条目。该书由 Addison-Wesley Professional 于 2024 年 11 月 20 日出版（ISBN-13 978-0-13-817218-3）。出版社介绍，Slatkin 在 Google 有多年工作经验。

对前线部署工程师（FDE）来说，这本书的价值不在于教你写出优雅的 Python。它提供了一份清单：接手别人的代码时该检查什么，以及怎样修复才不会破坏一个已经在运行的系统。

## 第三版有哪些新内容？

第三版从 90 条扩充到 125 条，其中 35 条是全新内容。内容跟进到 Python 3.13，`match`、dataclasses 等主题都用现代语法来写。

最值得花钱的是新增的两章：健壮性和性能。书中还深入讲解了 C 扩展和调用原生库，用于纯 Python 解决不了的性能问题。

作者维护着官方 GitHub 仓库 `bslatkin/effectivepython`，每个条目都有单独的示例文件或文件夹。因此这本书可以当参考书用：遇到问题时，翻到对应条目，直接运行示例。

## 应该按什么顺序读？

不要从头读到尾。准备修客户代码时，先读健壮性一章。这一章讲如何正确使用 try/except/else/finally，为什么不该捕获整个 `Exception` 类，以及异常链。

修复上文那条写入空行的管道，需要的正是这三点。接下来读函数一章：第 32 条建议用抛出异常代替返回 `None`，第 30 条提醒你传入函数的参数可能被修改。阅读陌生代码时，这两点都是常见的 bug 来源。

之后再读测试和协作两章，最后才是性能和并发。这样排序有原因：想让代码跑得更快，先要确保改动不会弄坏别的东西。

**要点:** 想让客户的代码跑得更快之前，先让它把错误报告清楚。

## 第一课：别让错误消失

回到那个 API 函数。用了 `except Exception: return None`，认证失败、超时和代码里的拼写错误看起来完全一样，都是 `None`。下面的代码片段仅作示意，把旧版本和按健壮性一章及第 32 条思路修改后的版本放在一起对比。

```python
# 修改前：所有错误都变成 None
def fetch_orders(client, day):
    try:
        return client.get_orders(day)
    except Exception:
        return None

# 修改后：只捕获你能理解的错误，保留异常链
class OrderFetchError(Exception):
    pass

def fetch_orders(client, day):
    try:
        return client.get_orders(day)
    except TimeoutError as e:
        raise OrderFetchError(f"获取 {day} 的订单超时") from e
```

在第二个版本中，超时会变成一个命名清晰的错误，`from e` 保留了根本原因。其他错误，包括拼写错误，都会暴露出来，而不是被吞掉。下次再出现空行，堆栈跟踪会直接指向原因。

刚到客户那里时，先做这件事：找出所有捕获 `Exception` 的地方，以及所有用返回 `None` 表示失败的函数。这份清单会告诉你系统在隐藏什么。

## 第二课：先补上测试，再逐步修改

第 109 条建议优先写集成测试，而不是单元测试。对没有测试的代码，这条建议很实用：一个从输入跑到输出、覆盖完整流程的测试，能先把当前行为固定下来，哪怕你还没弄懂里面的每个函数。

第 112 条讲如何封装依赖，使其便于 mock，适合那些要调用客户数据库或 API 的流程。

协作一章补充了两个渐进式修改的工具。第 123 条用 `warnings` 通知调用方某个函数的用法即将变化，给客户团队留出迁移时间。第 124 条用类型标注做静态分析，在代码运行前发现错误。

两者结合，就能一步步重构，不破坏正在运行的部分。

## 第三课：先测量，后优化

性能一章以第 92 条“Profile Before Optimizing”（先做性能分析，再优化）开篇。第 93 条讲如何用 `timeit` 做微基准测试，第 94 条讨论何时用其他语言替换 Python。这几条串起来是一个完整流程：找出代码真正慢在哪里，修复前后各测一次，只有在 Python 里已经无计可施时，才考虑 C 扩展。

并发同样需要谨慎。第 68 条建议把线程用于阻塞式 I/O，而不是用来并行计算。如果客户想靠加线程来加速一个繁重的数据处理任务，先读这一条，再决定是否答应。

## 谁该读这本书，对职业发展有什么帮助？

如果你写了几年 Python，但大多是自己的代码，这本书对你最有价值。准备转向 FDE 岗位的人也应该读，因为这份工作几乎总是从读别人的代码开始。

简历上不要写“精通 Python”，而要写具体的事：比如把过宽的 `Exception` 处理替换为有类型的异常，给没有测试的流程补上集成测试，再通过性能分析找到真正的瓶颈。

面试时，如果拿到一段陌生代码，先问怎么测量，再提出优化方案。这个问题会让面试官看出你在真实系统上干过活。

125 条都有用，但上文提到的十来条，已经足以改变你在客户那里第一周的工作方式。

**本周动手试试:**

- 克隆 bslatkin/effectivepython 仓库，运行第 32 条和第 30 条的示例。然后在你最近读过的代码库里 grep 'except Exception' 和 'return None'。
- 在当前项目里挑一个慢函数，改动之前先做性能分析（第 92 条）。修好后，用 timeit 对比新旧两个版本（第 93 条）。
- 给一个没有测试的流程写一个集成测试，然后再重构它的内部实现（第 109 条）。

## 信源

- [Effective Python: 125 Specific Ways to Write Better Python, 3rd Edition (InformIT)](https://www.informit.com/store/effective-python-125-specific-ways-to-write-better-9780138172183)

- [Effective Python: 125 Specific Ways to Write Better Python, 3rd Edition (effectivepython.com)](https://effectivepython.com)

- [bslatkin/effectivepython (GitHub)](https://github.com/bslatkin/effectivepython)
