# 吉多·范罗苏姆：Python 与编程的未来

> Python 之父回顾这门语言的设计初衷，并讨论编程语言与开发者工具的未来走向。

- 原标题：#341 – Guido van Rossum: Python and the Future of Programming
- 期数：第 341 期
- 发布日期：2022-11-26
- 嘉宾 / 主题：吉多·范罗苏姆
- 网页版：https://lexfridman-cn.pages.dev/ep/guido-van-rossum-2/
- 官方页面：https://lexfridman.com/guido-van-rossum-2/
- 精读字数：约 9060 字（阅读约 20 分钟）

> 本文为 lex.qiaomu.ai 基于原始访谈的 AI 改写中文精读，非官方翻译；引用请注明出处并链接网页版。

Python 3.11 发布前夕，CPython 团队给出的承诺是新版本比上一版快 10% 到 60%。一门三十多年前动笔的语言，靠改自己的底层实现拿到这个量级的提速，这份成绩单放在任何工程领域都算漂亮。

Lex Fridman 问吉多·范罗苏姆（Guido van Rossum）：这是怎么做到的。

他没接这个问题，先纠正了问题里的一个词。

> "CPython 是最后一个还站着的 Python 实现，也是历史上第一个，我三十多年前亲手写的那个。"（吉多·范罗苏姆）

"最后一个还站着的"，这句话说得很轻。一门编程语言通常可以有多个实现，Jython 跑在 Java 虚拟机上，IronPython 跑在 .NET 上，PyPy 用 Python 自己写自己。这些名字前面都得挂个前缀，因为不加前缀就不知道你在说谁。只有 CPython 不需要，它就是 Python 本身。

名字里的 C 是它用 C 语言写成。一门语言的默认实现，是用另一门语言写的程序。这件事在外行眼里很反常，在程序员眼里是常识，而这期节目的听众里，这两种人都有。

## 一条船上坐着两个人，其中一个只会钓鱼

Lex 把听众劈成了两类。一类是在船上钓鱼、从没听说过编程的人，另一类是写遍 C、C++、Python、Rust、Java，能把编程语言发展史倒背如流的硅谷顶级工程师。一条船，两个人，中间隔着整个计算机科学。

吉多选了先让钓鱼那位满意。

> "在海上，他显然是更有用的那个人。你得让他高兴。"（吉多·范罗苏姆）

怎么跟他讲？他手里一定有个手机，所以他对手机里发生的事情大概抱有怀疑。但他至少听说过手机里有一块小小的计算机，而编程语言就是告诉这块计算机该干什么的代码。再往下就别讲了：零和一，汇编，寄存器。讲这些等于在讨论人类语言的时候去研究声道、舌位和气流。语言学讨论"我爱你"这三个字的意思，不讨论你的舌尖抵在上齿背还是下齿背。

Lex 在这里插了一段自己的观察。他说中文母语者讲英语时常常分不清 L 和 R，他有个从没找语言学家验证过的猜想：汉语里这两个音本来就没有对立，而不同地区的人在发那个中间音时，有的听起来像 L，有的听起来像 R。吉多顺着往下接：斯拉夫语系里有个 j 音，英语母语者发起来很别扭。两个人聊了几句发音，然后一起把这个话题放下了。

放下得对。再往下就是舌头怎么摆了，而舌头怎么摆，和编程语言是什么，中间隔着好几层抽象。

## 编程语言是一份菜谱，但要喂给两个读者

吉多给出的定义是：**编程语言就是一份菜谱，一行行告诉你该怎么做出某样东西。**

买一条面包，切片，取两片，一片抹芥末，一片抹果酱，然后放肉，再放奶酪。你要把这件事说得足够清楚，清楚到没有任何一种理解方式会出错。

Lex 说他见过科学老师玩这个游戏：让学生写下做三明治的步骤，老师严格照着做，做到学生抓狂为止。学生的意思是"拿两片面包"，老师就真的把整袋面包都拿过来。

这个游戏暴露的正是自然语言和编程语言的根本差别。**在人类口语里，歧义是特性，不是缺陷。**它是人与人之间那场舞蹈的一部分，你说一半，我猜一半，猜错了笑一下，再补一句。律师喜欢歧义，因为歧义就是诉讼费。日常对话里歧义也会带来麻烦，但人们绕着走，靠上下文把它填平。

对着一个你爱的人说"我爱你"，对方不会试着编译这句话然后报错：请定义"love"。

> "不会。不过我猜我太太和我儿子听到这三个字，理解会非常不一样。"（吉多·范罗苏姆）

同样三个词，同一个句子，两个听众，两种含义。靠的还是上下文。换成律师，他会追问你二十个问题。

编程语言干的事，就是把这个上下文抽掉，把一份菜谱写成不管谁来读都只能读出一种意思的东西。

## 《Python 之禅》里那句"可读性很重要"是说给谁听的

Lex 提到那份被无数 Python 开发者当口诀背的东西，《Python 之禅》。里面有一句：可读性很重要（Readability counts）。

这句话初看很怪。代码是给机器看的，机器不在乎你缩进几个空格，你在中间插一行脏话它照样跑。可读性是给谁的？

吉多的回答把整件事翻了个面。**软件开发从四十年代末开始，就一直是件社交活动。**那个坐在实验室里独自写出天才代码的疯狂科学家，其实并不存在。就算真的存在，他打字的速度也快不到哪去，等到他写完最后一行，前面那几十行写了什么，他自己也记不清了。

所以写代码的时候，你同时在跟两个对象说话。

一本菜谱只有一个读者。作者写一次，印十万份，十万人照着做樱桃派。作者的全部目标是让厨房里那位业余厨师看懂。软件这个行当完全不同。软件太复杂，复杂到你不可能一次写对，所以必然有调试，必然有修改，必然有别人接手，也必然有一年之后的你自己回来看这段代码。

两个读者，两套要求，还经常打架。这就是为什么"代码风格"这种东西在别的行业听起来像个笑话，在软件行业却值得写进语言的核心文档。

## 调试是三个完全不同的东西，被同一个词装在一起

Lex 让吉多给调试下个定义。那个回答被切成了三层。

最浅一层是蠢错误。少打一个字母，括号没配对，路径写错。

中间一层是逻辑错误。程序完全按你写的跑了，跑出来的东西是错的，因为你想错了。

最深一层是这玩意压根没用。代码没错，逻辑没错，跑得又稳又快，只是它解决的问题不该被解决，或者解决的方式让产品变得没法用。

还有第四层，吉多专门拎出来说：**程序 99% 的时间表现完美，然后在某个边缘情况上突然发疯。这是今天几乎所有软件的真实状态。**好软件是这样，差软件那个 99% 会掉到 90% 甚至更低。

这句话往下推，得出的结论不太舒服。你今天用的每一个软件，包括那些支撑银行、医院和电网的系统，都有若干条路径是你从来没走过、也永远不会被走到的。软件工程的成熟，相当程度上是学会了在这些路径上放护栏，而不是假装它们不存在。

## 四个空格是怎么来的

看 Python 代码的第一眼，看到的往往是形状。左边那道阶梯，一层往里缩一点，像一份排版讲究的商务文档。

吉多的解释很朴素。任何文本的可读性都靠空间，把一份菜谱的所有项目符号删掉，句子揉成一整块，你在厨房里连"哪些是原料、哪些是步骤、这一步到哪结束"都分不出来。缩进就是给代码造出这种可见的分组：一段文字里套一段更小的文字，再套一段更小的。

问题在于缩进多少。缩进太深，三层之后屏幕上就没地方放字了。缩进太浅，一眼看不出这是第一层还是第二层。

答案是四。理由是古代排版惯例，加上八十年代那批显示器的典型宽度，两个约束一夹，四个空格落在中间。这是个妥协，谈不上定理。

Google 的 Python 代码风格指南用的是两个空格，据说 Google 的 C++ 代码也是。你不习惯的话，读起来会有点吃力，因为这种语言里，高层结构完全由缩进决定，缩进浅了，骨架就模糊了。经典 Unix 那边更夸张，一个 tab 八格，吉多觉得那看着别扭，三层之后就贴到屏幕右边了。

Python 的特殊之处在于，**缩进在这里属于语法的一部分，风格建议这个说法太轻了。**别的语言里，C、Rust、C++、Java，你把代码写成一整行，编译器照样给你编出来，只是同事会想打你。所以现实里 IDE 会自动帮你缩进，缩错了还会用红波浪线吼你。人类用工具把一条软规范硬成了事实标准，Python 只是把这条已经存在的规矩写进了语言定义。

## 花括号本来有可能出现在 Python 里

Lex 问了个历史问题：有没有那么一个平行宇宙，Python 是用花括号分块的？

"绝对有。"

吉多说，很长一段时间里他都确信缩进更好。就算把上下文全部抽掉，单看这个设计，他还是认为缩进更好，因为它减少了视觉噪音。

可惜上下文几乎就是一切。而编程这件事的上下文是：**大多数程序员都熟悉好几门语言，哪怕只精通一两门。**除了 Python，可能再加上 Fortran，其他所有主流语言，Java、Rust、C++、JavaScript、TypeScript、Perl，全用花括号标记代码块。Python 是那个异类。

异类是有代价的。一个从 Java 转过来的人，第一天写 Python 会觉得少了点什么，像走路时突然发现自己一直在无意识地甩手。

那为什么不改？

> "Python 里没有这个可能性。花括号在 Python 里已经用来干别的了，主要是字典。真要想换，会惹出一堆麻烦。"（吉多·范罗苏姆）

他还补了一句更根本的：你不能重新定义 C 让它是用缩进的，哪怕你打心底认为缩进更好。一门语言里，这种级别的东西改不动。比这细小得多的细节，想达成一致都已经够难了。Python 历史上真为 tab 和空格打过一场大仗，为四个空格还是两个还是八个吵过，最后的产物是一份推荐标准，外加一堆给不听话的人准备的选项。

## 时间旅行回去，他还是会选缩进

Lex 不放过他，把假设推到底：如果能穿回三十多年前，兼容性完全不成问题，Python 从头再来一遍，你还会选缩进吗？

会。理由有三条。

第一，它省下一对括号。而**这世界上配对的东西永远不够用**，省下来的括号可以拿去干别的（在 Python 里，它们后来确实去干别的了）。

第二，对完全没学过编程的人更友好。一门十三周的入门课，要把算法、数据结构、语法、变量、循环、函数、递归、类、表达式、运算符全塞进去。能少讲一个概念，就少一个让人分心的东西。分号是教学里最经典的麻烦：编译器每次都抱怨我分号放错了地方，或者我又忘了写分号。Python 没有分号，你忘不了它，也不会把它放到不该放的位置，因为你从一开始就没学过它。

第三，也是吉多自己补的反面：**强制严格，本身就是在教东西。**它告诉新手，编程是一件在意细节的事，你不能像写英语作文那样随便写。当然，新手要操心的细节还有很多，这条信息总能传达到。

## 美元符号是一块内存不够用的化石

Lex 换了个靶子：PHP 里变量前面那个美元符号。他说这玩意在他的理解里完全讲不通，而且打字累，还特别容易错。

吉多给了一段考古式的回答，讲的是 Unix shell 的来历。

最早的 shell 根本没有"给脚本传参数"这个概念。一个脚本就是几行文本，每行是一条命令，一个非常原始的命令处理器读它，把行首那个词当作程序名，剩下的一整行当作文本丢给程序自己去解析成参数。

后来人们想要变量了，尤其是想要参数。麻烦来了。你写下 program x y，这在原来的语义里已经有确定含义：x 是一个文件名。可你想说的是"x 是一个变量，它的值才是文件名"。这两件事必须能区分开。

而在五六十年代，写这个脚本解释器的程序本身是个极小的程序，得塞进很小的一块内存。最省事的做法是：逐字符扫描，看到某个特殊符号就跳到另一段代码，把后面一直到空格的东西读出来当变量名。选哪个符号？美元符号便宜、显眼、不容易和正常文本撞车。

它同时也是给人类读者看的。二十行之后你看到 xyz 前面挂着一个美元符号，立刻知道 xyz 是脚本的参数之一，硬盘上某个文件名这个身份被排除掉了。

PHP、Perl 都继承了这条血脉。IBM 更早的作业控制语言里也有类似的东西。这些设计活到了今天，活在支撑整个互联网后端的代码里。Wikipedia 里就有一大堆 PHP。

> "这就像生物系统。DNA 的内部机制已经稳定了几亿年，里面全是各种古怪的遗留物。如果你从零设计一个能自我复制的分子系统，绝不会那么干。"（吉多·范罗苏姆）

Lex 顺着这个比喻往下说：生物系统有很多奇怪的冗余，会以奇怪的方式出错，但在有机体这个层面上依然有韧性。代码没有这种韧性。

吉多不同意。他说你会惊讶于现代代码有多强的韧性。**哪怕是测试得很充分、实际运行良好的代码，里面也有很多地方是坏的，只是错误被一层层机制吸收掉了，包括最后那一层：用户。**用户被告知重启电脑，重启一下，问题就消失了。温和一点的版本是刷新页面，所有人遇到怪事都会下意识刷两三次，然后才承认这里真有问题。

Lex 说这个习惯得改，因为刷第二次通常只是把灯又关上了。吉多说，那就刷三次，这才是正确的教训。

## 一千行代码里到底有几个 bug

Lex 抛出一个想法：如果把运行核武器的代码、运行 Twitter、Facebook、Dropbox、Google、Windows 的代码，按每千行的 bug 数排成一张表，人们会不会被吓到。

吉多说这取决于你读没读过这个领域的研究。他记得上世纪八九十年代有过一批研究，覆盖了各种软件、各种语言、各种公司、各种开发风格，结论相当稳定：**在成熟的、被公认为质量很好的软件里，大约是每千行一个 bug。这已经是行业天花板。**

Lex 随即摆出一份数据，来自一家做编程分析的公司，视角是开发者端：平均每个开发者每一千行代码写出 70 个 bug，其中 15 个会流到客户手里；修一个 bug 花的时间是写一行代码的 30 倍；开发者 75% 的时间花在调试上；美国一年在找 bug 和修 bug 上烧掉 1130 亿美元。

吉多的第一反应是承认自己错了一个数量级。

紧接着他做了那个关键的区分。**每千行一个 bug，说的是经过长期测试、已经稳定下来的成熟软件。每千行 70 个，说的是你刚开始敲键盘的时候。**这两个数字根本不测量同一件事。拿它们放在一张图里比较，就像拿新生儿的哭闹次数和成年人的说话次数做对比，然后得出人类越活越沉默的结论。

他也顺手把这页数据看穿了：这是一家卖工具的公司做的内容。页面底部必然挂着一个"联系我们"按钮，潜台词是，你只要掏出比一千一百三十亿少一点的钱，我们就帮你把这个坑填上。

Lex 提到 Stack Overflow 上有一份同主题的报告，他想打开对照一下，页面显示"网站维护中"。

> "这挺讽刺的。"（吉多·范罗苏姆）

一个支撑了全世界最大问答社区的技术，在你想引用它的数据时告诉你它挂了。软件这东西的可靠性，大概就是这么回事。

## 你不可能在一年前就写好今天的发布计划

吉多接着解释那个 70 和 1 之间的落差从哪来。软件开发是极度迭代的。**没有人真的会做一份"一年后发布什么"的完整计划，然后把细节全部敲定。**因为那些细节本身拼起来就是一份程序，而只要它是一份程序，你的计划里就也会有 bug，也会有想错的地方。

所以你实际干的事是这样的：敲一堆代码，跑一下，看看它是不是你想的那个样子，不对就改，改完再跑。几十轮之后，剩下的那些错误，才轮得到"每千行一个"这个数字去描述。

Lex 在这里承认，那个 70 出现的时候他也吓了一跳。吉多的反应更平静：这个数字说明的是一件事情，代码在诞生阶段极其脏，脏到没法看，而工程的全部工作就是把这份脏慢慢洗干净。

## 他打字只用两根手指，而且不打算改

话题拐到了打字上。吉多说自己是个很糟的打字员，十个手指都在用，但用得很差。他从没上过打字课，也从没专门去纠正这件事。**第一次认真学 QWERTY 键盘的布局，是大学里第一门编程课，那时候用的是打孔卡。**他就用两根手指，一个键一个键地把代码戳出来。

Lex 说他有个观察方法：让任何一个程序员现场演示几行代码，你就盯着他按退格键的次数。有人会为了改行首的一个错字，往回退二十、三十、四十个字符，然后重新敲。熟练一点的会用方向键，或者动鼠标，但鼠标通常比方向键慢。很多人敲一个二十个字符长的单词，敲到一半发现开头错了，就把整个词退掉重来，有时候要重来三四次才对。

吉多在这里做了一个区分，这个区分挺重要。**打错一个字然后立刻改掉，严格说不算 bug。**但你确实为此浪费了时间，而且偶尔会有一个错字漏网。等你又往下敲了十行，你不知道那十行里哪一处有问题，只知道某处有问题。

这跟忘记初始化一个变量属于两个层面的问题。前者跑一遍就暴露了。后者在 Python 里也跑一遍就暴露了，现代编译器大多也能提前抓出来。真正麻烦的是第三种：变量你初始化了，名字也叫对了，但初始化的是另一个东西，或者你本意用的那个变量跟它重名了。这种东西编译器抓不到，测试也不一定覆盖得到，它会在某个深夜的线上环境里冒出来。

## 把退格键挪到拇指下面，是一个价值三十年的决定

Lex 顺势讲了自己为什么用 Kinesis 键盘：那种键盘把退格键放在拇指正下方。普通键盘的退格键在右手小指够得着的地方，每按一次你的整只手都要往外撑一下。很多人一辈子都在承受这个设计带来的疼痛，只是没意识到。

吉多问他，如果你在别人那儿，手边只有标准键盘怎么办。

Lex 的回答很实在：你可以一辈子随身带着键盘。他在飞机上要写代码或者大量打字的时候，会把笔记本摊开，再从包里掏出一把 Kinesis 摆在旁边。

> "我已经接受了自己是个怪人这件事。"（吉多·范罗苏姆）

这句话往下接的是一段更普遍的经验：很多人都有自己的一套舒适区配置，某些特定的设置能把生产力拉满。有人看电影要盖一条特定的毯子，他要的是那把键盘。它带他进入一个能专注的状态。

然后他补了一句：**他大部分时候都在努力用最先进的 IDE，但真正让他坐得住的，还是 Emacs。**

这才是这期节目里最有共鸣的一段，因为它咬住了一个所有技术人都要反复面对的问题：**该多紧地抱着顺手的工具，又该花多少精力去学新的。**

## 社区一年一换的兴奋，多数是噪音

Lex 说，社区给你的信号是嘈杂的。每年都有一批人为了新工具兴奋，而你必须做一个判断：这些东西是在定义新一代的工作方式，还是会改变编程这件事的形态，或者只是一阵风，吹完就散。

前端尤其明显。JavaScript 框架、构建工具、状态管理方案，一波接一波地来，一波接一波地走。

他举了自己的例子。Flash 里的 ActionScript，他认真学过，学怎么在里面写程序，学怎么在里面做图形动画。Java applet 也是，他当年写了相当多的 applet，真心觉得这可能是 Web 的未来。

结果不是。

吉多的回应值得逐句读。他没有安慰说"你那些时间没白费"，也没有附和说"那都是垃圾"。他说的是：**这类技术最终都会死掉，但它们最早引入或者最早让大众接触到的那批概念，会被留下来。**

Java applet 早就没了，可"页面里包含一小段代码，直接响应你的动作"这个念头，按下按钮、点开链接、鼠标悬停，一点没退场。Flash 当年做那些动画做得极其痛苦，后来被 JavaScript 的同类方案取代，那确实是更好的做法，但动画本身还在，只是换了个载体。

> "技术演化经常是这样的：那个最终被扔掉的阶段，恰恰是让你能起步的东西。没有螺旋桨飞机，就不会有喷气式飞机。"（吉多·范罗苏姆）

Lex 在这里插了一句反驳，而且插得对。从功能上看是这样，从用户角度看也是这样，可从写代码的那个人角度看呢？那些泡在 ActionScript 里的时间，那些熬在 Java applet 调试上的夜晚，怎么算？

他没能把这个问题问完。文字稿到这里断了。

## 什么能活三十年，什么活不过五年

不过这条线其实已经被前面的内容回答了，只是答案散在各处，得自己拼。

看吉多怎么谈 Python 的缩进。他说这件事在 Python 里已经没有重新讨论的余地，因为花括号已经在语言里干了别的活，动这一个决定会连带扯出一堆东西。他还说，哪怕你认定缩进更好，你也不能回头去改 C。**一门语言活到一定年头，它最重要的属性就从最优变成了稳定。**稳定意味着已经写下的几亿行代码明天还能跑，意味着所有人脑子里那套肌肉记忆还成立。

再看那笔每千行一个 bug 的账。这个数字之所以能压到一，靠的是时间。是无数次上线、无数次线上事故、无数次把边界条件一条条补上。一个项目真正的资产，往往藏在它踩过的、并且被修掉的那些坑里，漂亮的设计图纸反倒排在后面。

Flash 死在第五年，Python 活到第三十年，两者的差别在语法优雅程度之外。**关键变量是它有没有长出一整套让几百万人协作的基础设施：包的生态、标准库、风格共识、向上兼容的承诺。**语法可以被抄走，这套东西抄不走。

所以回头看那些两年就没人提的框架，判断标准其实很朴素：它只是把某件事做得更方便，还是引入了一个会一直留下来的概念？前者是工具，后者是思想。工具会被下一个工具换掉，思想会渗进下一代工具里，换个名字重新出现。

吉多在访谈里反复回到同一个词：上下文。缩进好不好要看上下文，因为大家都在用花括号。美元符号难用还得留着，因为上下文是五十年前那台内存不够的机器。一个技术决策是对是错，很少能在它被做出来的那一刻判断，得等十年，看它长出什么。

写代码的人从这条线里能落下一个有点反直觉的结论。**花在某个具体工具上的时间，可能真的会归零；花在理解它为什么这么设计上的时间，基本不会白费。**ActionScript 的三个晚上，如果只学了语法，那就是三个晚上；如果顺手想明白了为什么在浏览器里跑动画要这么费劲，那笔账十年后还在给你分红。

Emacs 也是一样。它四十多年没死，键位设置合理这个理由站不住脚，恰恰相反，那些键位是当年那台机器的键盘布局留下的化石。它活着是因为它允许使用者把编辑器改造成自己想要的东西，这个念头活得比它自己久，今天所有现代编辑器都在继承这条思路。

三十多年前，吉多在一个圣诞节前后开始写一个解释器，当时的动机只是想做点事情打发时间。**他没打算设计一门能拿奖的语言，他想设计一个自己愿意天天读的语言，然后坚持了三十年不改主意。**这个决定后来变成了几百万人每天上班要用的东西。

这件事里可复制的部分不多，但有一点：他选的那条路，缩进代替花括号，可读性大于简短，从头到尾没变过。中间被质疑过，被打过仗，被 Google 的风格指南用两个空格顶回来过，他都认了，但主干没动。

一份菜谱要喂给两个读者，一个是机器，一个是人。三十年来所有成功的语言都在回答同一个问题：当两者打架的时候，你站哪边。吉多站了人这边，代价是 Python 在性能上一直被人嘲笑。3.11 那个 10% 到 60% 的提升，是他们在不改语言设计的前提下，回头去改底层实现拿到的。

不推翻自己，只是把地基挖深一点。长跑选手和短跑选手最大的区别，大概就在这里。

- 看到"每千行 70 个 bug"这类数字，先问它测的是哪个阶段。开发中的代码和运行了五年的代码，差着一个数量级，混在一起比会得出完全错误的结论。
- 隔一段时间去读自己一年前写的代码。如果读得毫不费力，说明对方水平和你一样且思路一致；如果读得很痛苦，那些痛苦的地方就是你该改的写法。
- 评估一门新技术，别只看它让你写起来多爽，看它引入了哪个概念。概念会留下，语法会被下一个人推翻。
- 缩进、分号、命名风格这类争论，交给自动格式化工具，别在会议上花时间。人类的判断力应该花在算法选择和边界条件上。
- 把"一次写对"这个念头从流程里删掉。既然成熟代码也有千分之一的错误率，那就在架构上预留返工的空间，别指望这次不出错。
- 退格键的位置值得认真对待。如果你每天要按几百次，把它挪到拇指下面，这个改动的投入产出比比大多数效率工具都高。
- 不要因为一个工具"过时了"就否定自己学它的那段时间。检查一下你从里面带走了哪个概念，那个才是真正的收获。
- 选定一套自己坐得住的工具，别每年重装一遍环境。舒适区省下的注意力，可以全部投给真正难的问题。
