首页 › programming › 一定有更好的写法:Python惯用法神课

一定有更好的写法:Python惯用法神课深读

你有没有过这种经历:接手一个项目,打开代码一看,缩进七层、循环里套计数器、字典手工判空,功能都对,但读起来像在啃一行行汇编。语法没错,逻辑没错,可就是一股浓浓的「外语腔」——就像一个英语很好的人,说话却句句都是中式语序。

今天要深读的这场演讲,就是治这个病的。Raymond Hettinger 的《Transforming Code into Beautiful, Idiomatic Python》,2013年3月在 PyCon US(Santa Clara)上录制,由 PyCon 官方频道发布,是 Python 社区公认的「必看神课」之一,十多年过去仍在各类「Python开发者必看清单」里被反复推荐。Hettinger 是 Python 核心贡献者,itertools 模块作者、collections 模块(defaultdict、namedtuple 等)的主要作者,sorted() 的 key= 参数设计背后也有他的推动——换句话说,你现在每天用的不少「Python味」,就是这个人参与定义的。

在 AI 能一行行吐代码的今天,这场十二年前的演讲非但没过时,反而更值钱了:生成代码容易了,判断代码「地道不地道」成了人类工程师的稀缺能力。下面进入正题。

视频背景卡片

项目 内容
视频标题 Transforming Code into Beautiful, Idiomatic Python
频道 PyCon US(官方频道)
发布时间 2013年(PyCon US 大会演讲实录)
时长 约49分钟
播放量 数百万级(平台实时数据以 YouTube 页面为准)
核心主题 用 Python 惯用法重写「生硬代码」,让代码更快、更短、更可读

说明:本文基于视频内容与公开资料撰写,播放量等平台动态数据以 YouTube 实时页面为准。

核心内容拆解

1. 这不是语法糖课,是一门「集体智慧」课

很多人把这场演讲当成「语法技巧合集」,其实低估了它。Hettinger 开篇就把惯用法(idiom)定义为社区沉淀下来的最佳表达方式——不是某个天才的发明,而是成千上万程序员在真实项目里试错后收敛出的共识。他的口头禅全场回荡:"There must be a better way!"(一定有更好的写法)每当你写出生硬的代码时,先别急着往下敲,停一秒,想想社区是不是已经给出过更好的答案。

他还有一个非常动人的观点(视频原话大意):如果你是专家,请回头去改善集体的智慧——去 StackOverflow 回答问题、去改文档。他本人就是这么做的,他在 StackOverflow 上的 Python 回答至今仍是大量开发者的入门教材。

延伸分析:这其实是编程语言学习里最容易被忽略的一层。学语法是「把句子说对」,学惯用法是「说得像母语者」。母语者扫一眼代码就能闻出「外来者」的味道,而代码的地道程度,直接决定了团队 review 的速度、新人上手的成本、以及三年后你自己回读时的痛苦程度。惯用法不是风格偏好,是沟通成本问题。

2. 遍历的六次进化:把「计数器思维」扔掉

演讲最出圈的部分,是他把最常见的 C 风格循环逐一「进化」。这是整场戏肉,值得完整梳理(示例为笔者依演讲要点整理):

# 生硬:把机器思维带进了高级语言
for i in range(len(names)):
    print(names[i])

# 地道:直接表达「对每个名字做什么」
for name in names:
    print(name)

在这个基线上,他连续展示了六种场景的惯用改写:需要下标时用 enumerate(names);同时遍历两个序列时用 zip(a, b);倒序遍历用 reversed(names),而不是手写 range(len(names)-1, -1, -1) 这种反人类的东西;有序遍历直接 sorted(names, key=...);去重遍历用 set(names)。

他的核心主张可以概括为一句话:循环应该表达「我要对什么做什么」,而不是「计数器怎么走」。for i in range(len(xs)): xs[i] 这种写法,本质上是把 C 语言的机器模型原封不动搬进了 Python——用他的话说,就是别在任何语言里写别的语言(大意转述)。

3. 字典是 Python 的灵魂:计数、分组、映射三件套

Hettinger 对字典的偏爱贯穿全场。他演示了两类最高频的场景。第一类是计数:

# 生硬:手工判空
counts = {}
for ch in "abracadabra":
    if ch not in counts:
        counts[ch] = 0
    counts[ch] += 1

# 地道:get 一行搞定
counts = {}
for ch in "abracadabra":
    counts[ch] = counts.get(ch, 0) + 1

第二类是分组——把元素按某个键归类到列表里。生硬写法要判断 key 是否存在再 append,地道写法是 defaultdict(list),一行解决。他还强烈推荐 namedtuple:与其让函数返回一个裸元组、调用方靠位置去猜第几个字段是什么,不如给每个字段起名字,代码自带文档。

延伸分析:这场演讲之后,社区沿着这个方向继续标准化——collections.Counter 把「计数惯用法」直接做成了类型,一行 Counter(s).most_common(3) 连排序都替你办了;后来的 dataclasses 和 typing.NamedTuple 则接棒了 namedtuple 的思想。你今天觉得「Python 写起来真顺手」的很多瞬间,都是这条「惯用法 → 标准库」流水线的产物:先在社区形成最佳实践,成熟了就沉淀进语言。

4. 选对数据结构,性能是白拿的

这场演讲另一个被严重低估的部分:很多性能优化根本不需要算法天赋,只需要选对数据结构。Hettinger 给了三组经典对照:

场景 慢的做法 惯用做法 差异本质
成员测试 x in big_list x in big_set 线性扫描 vs 哈希一步定位
左端弹出 queue.pop(0) deque.popleft() 全体搬家 vs 常数时间摘头
字符串累积 s += part ''.join(parts) 反复整体拷贝 vs 一次成型

他现场展示的数据量级差异相当震撼:成员测试从「百万条逐个比对」变成「哈希表一次命中」,复杂度从 O(n) 掉到常数级。而且这些改写不牺牲可读性,反而更短。

延伸分析(视频外的背景补充):这些差异不是玄学,是 CPython 的内部实现决定的——list 是动态数组,中间插入或头部删除意味着后面所有元素整体移位;deque 则是分块双向链表,两端操作天然便宜。所以「选结构」本质是「理解结构背后的内存布局」。同样一行代码,复杂度天差地别,而性能税是在写下的那一刻就交完了的,不是运行时才浮现的。

5. 扁平优于嵌套:缩进是一种认知税

Python 之禅(PEP 20)里有一条:Flat is better than nested(扁平优于嵌套)。Hettinger 把它落成了具体的手法。第一招是 guard clause(卫语句)提前返回:与其把主逻辑包在层层 if 里,不如先把不合法的情况一个个挡在门口,主逻辑零缩进平铺直叙。第二招是 Python 特有的 for-else:循环体后面跟一个 else,循环「没被 break 打断、自然跑完」时执行——写搜索类逻辑时,能优雅处理「没找到」的分支,不用引入 found 标志变量。

他还有一个全场反复强调的原则:one logical line of code——把「一个逻辑动作」写成一行。不是为了压行数炫技,而是因为当一行代码只表达一个完整意图时,大脑可以整行整块地读,像读句子一样;反过来,五行拆开的中间变量、或者七层嵌套的深渊,都强迫读者在脑内手动维护一个「状态栈」。

延伸分析:缩进深度其实可以直接当作复杂度的量化指标。嵌套每加深一层,读者要在脑内多压一层上下文,review 出错率随之上升。后来社区流行的「提前返回 + 小函数」重构法、甚至 CodeAesthetic 那支《Why You Shouldn't Nest Your Code》爆款视频,讲的都还是这件事——认知税是真实存在的,而且按嵌套层数复利计息。

6. 惰性与缓存:让代码「少干活」

Hettinger 专门讲了一类反直觉的优化:最快的代码,是不做不必要工作的代码。第一件武器是生成器表达式——(x*x for x in xs) 不会先把整份结果建成列表再喂给下游,而是流式吐出,处理超大文件或无限序列时内存占用完全不是一个量级。配套的 itertools 工具箱(islice、chain、groupby……)就是为「流式、惰性」组合而生——他本人正是这个模块的作者,现场他半开玩笑地提醒大家去读文档(大意)。

第二件武器是 functools.lru_cache:一个装饰器,让重复调用的函数自动走缓存。他演示了给递归函数加一行装饰器、性能从指数级掉到线性级的例子——一行代码,胜过手写一套缓存逻辑。

延伸分析:这两件事看似不相关,内核却是同一种品味——识别重复劳动,然后把它交出去:交给惰性求值(不提前干以后可能用不上的活),交给缓存(已经干过的活不再干第二遍)。这套思路放到系统设计层面就是流式计算和 memoization,放到个人工作流层面就是自动化脚本。品味是相通的。

7. with 与上下文管理器:把「人肉纪律」下沉为「语言结构」

资源管理部分,Hettinger 展示了 with 语句的正确打开方式。没有 with 的世界,打开文件要 try/finally 里手写 close(),用锁要先 acquire() 再记得 release()——每一处都依赖工程师「一定记得做」,而忘了的那次就是事故。有了 with:

with open('data.txt') as f:
    process(f)

with lock:          # 自动加锁、自动释放,想忘都忘不掉
    update_shared_state()

无论中途 return、break 还是抛异常,清理动作必然执行。他强调这类惯用法的价值在于:把正确性从「纪律」问题变成「结构」问题——不靠人自觉,靠语言机制兜底。

延伸分析:这是整场演讲里最具工程哲学味道的一点。好的语言特性不是让正确的代码写得更快,而是让错误根本写不出来。上下文管理器、后来的 async with、contextlib 工具箱,都在扩展这个思路。做工程的人可以记住这个判断标准:如果你的流程靠「大家都小心点」来保证,那它迟早出事;把它做成结构,才是真的解决。

8. 十二年后的再解读:为什么 AI 时代更该看这场演讲

最后说说我的延伸判断。2013 年的代码示例,有些 API 已经演进(dict.iteritems 早成了 items),但这场演讲的生命力反而越来越强,原因有三层。

第一,它教的是表达方式,不是版本特性。enumerate 和 zip 会换名字,但「用数据结构表达意图、用惯用法降低沟通成本」的思想永不退役。Python 后来的 match-case、dataclass、类型标注,全都可以用演讲里的同一套品味去审视。

第二,AI 编程时代,审美的稀缺性上升了。2026 年的今天,让模型生成一段「能跑的代码」是几秒钟的事;但判断这段代码是地道的 Python 还是「用 Python 语法的 C」,需要受过训练的眼睛。这场演讲几乎可以直接当成 code review 的标尺:range(len())、手工计数器、pop(0)、七层缩进、手写缓存——看到就触发改造。换句话说,它培养的正是这个时代最值钱的能力:鉴别与品味。

第三,它演示了一个专家如何反哺社区。Hettinger 把多年功力压缩成五十分钟,配上文档和 StackOverflow 回答,让整个生态的水位上升。技术演讲浩如烟海,能被一代代新人反复挖出来重看的极少——因为它们的作者卖的不是信息,是经过时间检验的判断力。

知识结构图

flowchart TD
    A["Python惯用法:地道的代码"] --> B["遍历进化"]
    A --> C["字典惯用法"]
    A --> D["数据结构选型"]
    A --> E["控制流扁平化"]
    A --> F["惰性与缓存"]
    A --> G["资源管理"]

    B --> B1["直接迭代对象"]
    B --> B2["enumerate 与 zip"]
    B --> B3["sorted 与 reversed"]
    C --> C1["defaultdict 计数与分组"]
    C --> C2["namedtuple 自文档化"]
    D --> D1["set 成员测试常数级"]
    D --> D2["deque 左端操作常数级"]
    D --> D3["join 一次成型拼接"]
    E --> E1["卫语句提前返回"]
    E --> E2["for else 搜索模式"]
    F --> F1["生成器表达式流式处理"]
    F --> F2["itertools 惰性工具箱"]
    F --> F3["lru_cache 自动缓存"]
    G --> G1["with 上下文管理器"]
    G --> G2["锁与事务的自动清理"]

实战启示

看完这场演讲,别只收藏。给你一套可以直接落地的行动方案。

第一步:给团队立一条 review 红线。 把这几条写进 code review 清单:出现 range(len(xs)) → 改直接迭代或 enumerate;手工判空计数 → defaultdict 或 Counter;pop(0) → deque;循环里 += 拼字符串 → join;列表大成员测试 → set。每条都是机械替换,风险低、收益立竿见影,特别适合作为团队「Python 味」改造的起点。

第二步:重构按三段走。 先跑测试锁住行为(没有测试先补冒烟用例);再做机械替换,一次只改一类惯用法,diff 干净到 reviewer 三十秒能看完;最后对涉及 set 化、缓存、生成器的改动跑一次基准测试——绝大多数情况是白拿的性能,但缓存要留意失效策略,别把正确性优化没了。

第三步:把品味喂给 AI。 现在很多人的日常是让 AI 写代码。做法很简单:把 PEP 20 之禅和你的惯用法清单写进项目规范或 prompt 模板,明确要求「用惯用法、早返回、扁平化、标准库优先」。同一份需求,产出质量差距肉眼可见。AI 降低了写代码的门槛,也放大了「会不会验收」的差距——而这场演讲就是最好的验收教材。

第四步:迁移这个学习方法。 换任何一门新语言时,别先背语法,先搜「language idioms」和官方风格指南(Python 是 PEP 8,Go 是 Effective Go),把社区的「母语口音」先装进脑子。写对是及格线,写得地道才是专业。

延伸学习路径

  1. PEP 20 – The Zen of Python(peps.python.org/pep-0020/)— 全场演讲的思想底本,19 条之禅值得贴在工位上,终端里 import this 即可阅读。
  2. Raymond Hettinger 的其他 PyCon 演讲 — 在 YouTube 搜索 "Raymond Hettinger PyCon",推荐《Modern Python: Big Ideas and Little Code》与历年「What's New in Python」系列,是同一套品味的持续更新。
  3. 《Fluent Python》(2nd Edition,Luciano Ramalho 著) — 这场演讲的「书版放大器」,惯用法背后的语言机制全部展开讲透,Python 进阶第一书。
  4. Python 官方文档:collections / itertools / functools 章节(docs.python.org)— 演讲里每个武器的一手说明书,写得极好,作者本人就是文档主力。
  5. CodeAesthetic《Why You Shouldn't Nest Your Code》(YouTube 站内搜索标题)— 十分钟讲透嵌套问题,与本场演讲「扁平优于嵌套」互为攻玉他山。

一句话总结

这门课真正教你的不是几个语法技巧,而是一种肌肉记忆:每写下一行生硬代码时,先停一秒问自己——There must be a better way。

参考来源

  1. Raymond Hettinger – Transforming Code into Beautiful, Idiomatic Python(YouTube 站内搜索,PyCon US 官方频道发布) — 本文分析的核心视频
  2. PEP 20 – The Zen of Python — 演讲思想源头,Python 之禅原文
  3. Python 官方文档:collections — defaultdict、namedtuple、Counter 一手资料
  4. Python 官方文档:itertools — 惰性迭代工具箱,Hettinger 本人参与编写的模块
  5. Python 官方文档:functools — lru_cache 等缓存工具官方说明

本文由「51学通信」技术团队出品。51学通信由通信行业18年老兵爱卫生(微信ID: gprshome201101)维护,聚焦5G/6G核心网、3GPP标准解读、通信前沿技术深度分析。 出版作品有《5G核心网原理与实践》《IMS原理与实践》等4本通信专业书籍。知识星球「51学通信」提供深度技术答疑和行业洞察。

[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。

51学通信知识星球海报-扫码加入