第 224 期·中文精读

特拉维斯·奥利芬特:NumPy、SciPy、Anaconda、Python 与科学计算

Travis Oliphant: NumPy, SciPy, Anaconda, Python & Scientific Programming

NumPy 作者回顾 Python 科学计算生态的诞生,从开源社区维护谈到 Anaconda 与数据科学的普及。

官方音频00:00 / --:--
中文精读由 AI 基于原始访谈改写,非官方翻译,可能存在偏差或错误;表述以原音频与官方文稿为准。说明

2005 年,杨百翰大学的一位助理教授发现自己处在一个尴尬的位置:他报名开了一门课,学生先选了又退,最后一个人都没剩下,他手上多出了一整个学期的空白时间。他决定用这段时间做一件事,把当时社区里分裂成两派、互不兼容的两套数组库,捏成一个所有人都能用的东西。

这件事最后变成了 NumPy。

它今天几乎是 Python 科学计算的空气和水。PyTorch 的张量、TensorFlow 的运算、Pandas 的底层,所有用 Python 做过数值运算的人,都踩在它上面。写第一版的人用了四个月把代码敲出来,又用了十四个月才让它真正能用,而且从来没有因为这件事拿到过一分钱工资。

这个人叫特拉维斯·奥利芬特(Travis Oliphant)。他后来还写了 SciPy,创办了 Anaconda,现在在做一件更冷门也更难的事:试着让开源软件本身能持续赚到钱。

四年级的 BASIC,和一台 2K 内存的电脑

特拉维斯写的第一段程序,是四年级课上的一段 BASIC 循环,跑在 Atari 800 上,用 GOTO 语句跳来跳去。他对编程最早的哲学冲击也来自那个时候。老师告诉他,不要用 GOTO,那不是好的软件工程实践,写代码是有规则的。

他当时的感觉是,哦,原来这个游戏是有规则的。这种感受一直等到高中选修 Pascal 的 AP 计算机科学课才再次出现。至于 C 语言,他到了大学才第一次正经接触。

真正让他对编程产生感情,是十岁那年。父亲买回一台 TI Timex Sinclair,本来只是冲着电子表格去的,但他硬是让父亲加装了 BASIC 扩展。那台机器只有 2K 内存,他们额外买了 16K 扩展包。存储靠磁带机,数字位会变成声音录进去。他至今还记得那个声音。

十二岁时家里换了 TI 994A,机器有精灵图、有图形、有音乐。你能编程让它唱歌。他说就是在那台机器上,他第一次真正爱上了编程。

他从小就喜欢数学,但他对这件事有一套挺清醒的判断:很多人觉得自己不喜欢数学,其实是因为小时候接触数学的方式就是死记硬背。乘法口诀背得熟的、短期记忆好的,就被判定为数学好。他自己恰好短期记忆不错,所以一路都被认为数学好。真正吸引他的,是解题的过程。计算机在他眼里就是解题能力的延伸。

不过他那时候并不觉得电脑是大脑的延伸。太原始了。从一个念头到真跑起来,中间隔着一大堆繁琐劳动。这种门槛今天仍然存在,只是低了一些。也正是这一点,让他在很多年后遇到 Python 时有一种强烈的感觉:我可以直接用它思考。

学一门外语,和学一门编程语言,是一回事

访谈中间有一段很有意思的岔路。莱克斯和特拉维斯聊到了语言和思维方式的关系。特拉维斯母语是英语,第二门流利的语言是西班牙语。他说起那种感觉:有一天你开始在梦里说西班牙语,然后你开始用它思考。语言不只是工具,它会带着你往某些思维路径上走。

莱克斯接上来说他自己俄语流利,俄语背后压着两次世界大战、几千万人饿死、承诺被反复撕毁的历史。所有那些痛苦和忧郁都在语言里,在那些悲喜交加的歌曲里,在押韵诗歌的传统里,在那种从爱到恨反复摆荡的情绪光谱里。陀思妥耶夫斯基和托尔斯泰都写俄语,但陀思妥耶夫斯基其实不太依赖语言的音乐性,所以他翻译成英文之后仍然能读,因为故事和哲学密度本身承担了大部分工作。可诗歌就没法翻译了。

特拉维斯顺着这个说了一句挺重的话:这个世界上有很多天才,我们永远接触不到,只因为科学和技术世界的通用语是英语。很多聪明人永远冒不出水面,我们就是碰不到他们。这也是他倾向于支持技术民主化、支持那些让普通人手里多一件工具的东西,而对集权式结构抱有本能警惕的原因之一。

然后话题自然地切到了 Python。Python 是特拉维斯第一门真正享受用来的语言,有意思的是,莱克斯也有同样的感受。两个人的体验出奇一致。

1997 年,回头看自己写的代码还能读懂

特拉维斯第一次接触 Python 是 1997 年,那时他在 Mayo Clinic 读生物医学工程博士。之前他是电气工程背景,做过卫星数据,用过 MATLAB、Perl、VMS 上的脚本,以及 Fortran 周边的一堆工具。作为一个常年处理数据的人,他对语言的选择其实很挑剔。

1997 年他把能看到的语言都过了一遍,Yorick 也看过,最后留下来的只有 Python。原因是它有两个东西,让他没有把它过滤掉。

第一是数组。1995 年,MIT 毕业生吉姆·胡古宁(Jim Hugunin)写了一个叫 Numeric 的库,那是 Python 里第一次有数组对象,而且是一维以上的、二维三维四维都能表示的数组。今天的说法叫张量,特拉维斯说他至今还是坚持这种东西应该就叫 ndarray,这场命名之争他输了。

第二是复数。很多编程语言的作者会觉得,复数不过是两个浮点数,用户自己搭一下就行。特拉维斯不这么看。对一个做电气工程、天天和 FFT 打交道的人来说,复数是把物理世界和数学世界连起来的一种代数结构。如果语言里没有它,每个团队都要自己重新实现一遍,而且实现方式各不相同,互相之间还合不到一块去。

真正让他“爱上”Python 的,是 1998 年发生的一件小事。他回头看自己 1997 年写下的代码,居然还能看懂。而三年前用 Perl 写的代码,回头看完全想不起来当时在说什么。

他没有变成一个 Python 专家,但他发现自己可以持续用它,而不需要成为专家。这个差别,是他最终押注 Python 的根本原因。

Python 用缩进代替花括号,这在当年是个不小的飞跃。特拉维斯承认这个设计有具体麻烦:复制粘贴容易出错,制表符和空格混用会出问题,早期终端处理不好。他说自己几乎没有真正被困扰过,反而很喜欢那种干净:视野里没有多余字符,看一段代码就是在看它本身。

Perl 也追求简洁,但那种简洁走向了另一个结果。Perl 社区有一种风气,谁的代码写得越紧凑、越短、越接近谜语,谁就越厉害。到最后写 Perl 变成了一件必须成为专家才能读懂的事。Python 允许你不必成为专家就能干活。特拉维斯用了一个很贴切的说法:写 Python 的时候,你可以调动自己每天都在用的那个“英语语言中枢”,不需要额外开一个脑区。

他还提到一个一直好奇的问题:如果一个人母语是日语或中文,用非拉丁字符长大,学编程语言的时候会是什么体验?Python 语法里的那些英文单词能不能借用他们的母语中枢?他说自己完全没研究过这件事,不知道答案。但他猜,这大概不容易。

被低估的 APL 传统

聊到数组编程,特拉维斯把话题往回拉了几十年。

这种“把整个数组当一个对象来运算”的思路,最早的语言是 APL,诞生在 1960 年代。APL 是一个天才式的设计,也是一个注定小众的设计。它不只引入了新符号,还引入了全新的键盘,因为 QWERTY 键盘上打不出那些字形。那个年代键盘布局还没那么固化,所以设计者的思路是,那就换一个键盘呗。结果这件事显然没有成。

APL 社区还有一种文化,跟 Perl 很像:谁会写更短的代码谁厉害。有人以能用 30 个字符写出“生命游戏”为荣。APL 里有表示求和的符号,有“副词”这种概念,本质上相当于一个操作符上的高阶方法。用这套工具,人真的会开始按维度思考世界。

特拉维斯说了一句挺值得琢磨的评价:如果一个人只受过传统计算机科学训练,他很容易觉得,n 维数组不过是个实现细节。可你真的把它做成语言里的一等公民之后,人的思维方式是会改变的。

他顺手吐槽了一下微软。微软文化上一向不太懂数组编程,所以过去很多年反复错过这一块。当然现在好一些了。

MATLAB 是这条传统的继承者。特拉维斯用得很多。他在 MATLAB 之后用 Numeric,再用 SciPy 把缺的东西一点点补上。

那个没人报名的学期,和 NumPy 的诞生

2004 年,特拉维斯在杨百翰大学做助理教授,正卡在终身教职的考核期上。评审委员会给他的反馈是:你做得不错,论文也在发,但你花在开源上的时间有点多了,能不能少做点那个,多做点论文和经费申请。

与此同时,他目睹了一件让他很不舒服的事。科学计算社区分成两派。一派用 Numeric,一派用新的 NumArray。NumArray 是哈勃望远镜团队在 Perry Greenfield 带领下推动的,他们要用 Python 处理哈勃的图像,但 Numeric 的数组类型不够丰富、广播语义不够稳定、操作不够多。所以他们另起炉灶。

然后 ND Image 出来了,一个图像处理库,里面有形态学工具,膨胀、腐蚀、开运算、闭运算。作为医学影像背景的人,特拉维斯知道这些东西有多重要,他自己一直想在 SciPy 里加,但一直没时间。

问题是 ND Image 只支持 NumArray。

于是整个生态裂成了两半。Numeric 和 NumArray 的数据互不共享,一个进程里可以同时存在一个 G 的 Numeric 数据和一个 G 的 NumArray 数据,两份内存、两套代码、两个互相做重复劳动的社区。

在特拉维斯看来,这是技术层面的事情,但后果是社区层面的:一个刚起步的年轻群体正在自我消耗。

恰好那年,他原计划开的一门数字 MRI 课没人报,学生先报了名又退了,最后一个人都没有。他手上多出了一整个学期的空白。

他决定把这个分裂合上。

他花了四个月写出 NumPy 的第一版,又花了十四个月让它真正可用。他后来回顾说,那四个月的高强度编码是全程最艰难的部分。NumPy 里最核心的技术贡献,是一个叫 dtype 的新类型对象。除此之外,他把广播(broadcasting)的语义真正定下来,把原来分散在 Numeric 和 NumArray 里的能力整合到一个数组对象里。

最折磨人的部分不在技术,在沟通。

为了让两派都愿意过来,他必须同时兼容 Numeric 和 NumArray 两套 API。今天 NumPy 里很多看起来莫名其妙的复杂度和角落设计,其实是那段历史的化石,为的是照顾当时两个社区的使用习惯。如果从零设计,很多东西不会写成那样。特拉维斯说这话的时候没有抱怨的意思,他是在讲一个普遍规律:你看到代码里某个奇怪的设定,先别急着问为什么是这样,它很可能是一个历史产物。

他对这件事的态度被莱克斯称赞为“有共情、没有自我”。莱克斯说,你看 JavaScript 框架的分裂,一个框架派生出无数分支,每次都在重复同样的故事。要真的把一群人聚起来,你得先放下自己的 ego,真正去听。不只是听,还得真的理解对方为什么喜欢某个 API、某种命名风格、某种调用方式,然后才能在新的东西里保留那种吸引力。

特拉维斯补充了一点:光听还不够,你得有足够的热情真的把它做出来。敷衍地点头说“我听到了”,然后什么也不动,这套办法没用。他说这对他来讲是一种人生哲学,他一生都在追求这种对他人价值的珍视。

2006 年,他知道这件事成了

NumPy 发布之后,一开始还是不确定的。六个月左右,开始有人加入。弗朗塞斯克·阿尔特德(Francesc Alted)、罗伯特·科恩(Robert Kern)、查尔斯·哈里斯(Charles Harris),这些人后来成为 NumPy 的中坚力量。特拉维斯说他们是真正的无名英雄,得到了应该得到的关注远远不够。

他自己当时也需要支持。他说那个阶段他其实最需要的是鼓励,这些人通过贡献代码来给他鼓励。

真正让他确定“我们成了”的时刻发生在 2006 年。那一年,约翰·亨特(John Hunter)决定让 Matplotlib 直接依赖 NumPy。在那之前,Matplotlib 支持 Numeric 和 NumArray 两种后端,用户装哪个它就适配哪个。

亨特把这条路砍掉、只留 NumPy 的那一天,特拉维斯说他心里清楚:这事稳了。

接下来 2006 到 2009 年,是一段加速期。他形容自己被 NumPy 后来的影响震住了。他知道这个东西会有用,完全没想到会这么有用。

原因还是那个词:语言。人开始用 NumPy 的方式思考问题了。一旦数组作为一个整体对象进入你的思维,你看数据的方式就跟以前不一样。这打开了全新的一整套思路。

SciPy:一个偷偷想做成操作系统的库

SciPy 其实比特拉维斯写 NumPy 更早开始。1998、1999 年,他还在读博,每天用 Python 处理医学影像,一边用一边发现手边缺东西:没有常微分方程求解器,没有数值积分,没有优化器。

他当时的做法很像一个毛头小子。他在邮件列表上看到有人在讨论,要是有一个优化库就好了,要是有个积分库就好了。他想,反正我自己也要用,那就我来写吧。

他开始给 Python 写扩展模块,第一个是读 DICOM 格式的 IO 包。这是他第一次写 C 扩展。他觉得这个组合很有意思:Python 是脚本语言,你能用它做高层思考;C 是底层语言,你能用它补上 Python 缺的部分。圭多·范罗苏姆(Guido van Rossum)当年写过一篇讲引用计数的文章,没有那篇文章,特拉维斯说自己会完全迷失。

然后他遇到了 Netlib。那是 1960、1970、1980 年代积累下来的几百个 Fortran 程序,全部是已经被人写好、测试过的数值算法。Fortran 77 其实是一门挺不错的语言,Fortran 90 他甚至觉得是自己最喜欢的 Fortran 版本,因为它有复数、有数组,语义层次够高。你不会想用它从头写一个程序,但写一个子程序完全没问题。

于是就有了一条非常务实的路径:把 ODE 求解器包成 Python 模块,把 Quadpack 积分包出来,一个模块一个模块地做。这些东西最初是分开发的,你要用哪个就单独装。后来打包成了一个叫 multipack 的东西。

1999 年,一个高中生写了 multipack 的 Windows 安装包。这个高中生叫罗伯特·科恩,他后来成了 NumPy 的核心贡献者。特拉维斯说,Windows 安装包出现的那一刻,他第一次意识到了一件事:二进制安装包对用户的增长有决定性影响。

这个认识后来直接影响了 Anaconda 的诞生。

2001 年,SciPy 正式发布。艾瑞克·琼斯(Eric Jones)和特拉维斯·沃特(Travis Vaught)在奥斯汀创办了 Enthought,把特拉维斯手里的 multipack、Perry Peterson 写的 F2Py,还有其他一些散件整合到一起,装上就能用。

SciPy 最初的愿景其实很大。他们想做的是一个完整的研发环境,有数组、有数学、有绘图、有 REPL。换句话说,他们想把 MATLAB 整个搬进 Python。

最后这个愿景没有完全实现。原因很有意思。

特拉维斯说,开源社区有一个内在的协作规模上限。人数少的时候,大家协作得很好。一旦成功、人变多了,共识机制就开始失效,出现政治,出现大组织里那种难以决定方向的困境。做基础设施是分布式协作最擅长的事,可要把基础设施拧成一个完整的产品交付出去,那需要产品管理,而产品管理恰恰不是这种协作模式自然长出来的东西。

绘图的那部分后来由约翰·亨特单独承担下来,就是 Matplotlib。亨特当时在量化金融行业工作,觉得需要更好的绘图工具,于是自己动手写了一个。2001 年发布,和 SciPy 是独立的库。

SciPy 会议也是 Enthought 在 2001 年创办的,五十来个人,大部分是实践中的科学家,关心的是怎么用代码把研究做得更好,以及怎么不再依赖 MATLAB。这个会议一直办到今天,是很多人最喜欢的会议之一。

特拉维斯在这里补了一句挺关键的自我定位。他说他本人并不反对专有软件,也不是开源原教旨主义者。他不喜欢 MATLAB 的地方非常具体:他写了代码、发表出去,然后告诉别人“你要跑我的代码,必须先买一个 MATLAB 授权”。这个问题驱动了他后面二十年的几乎所有选择。

读斯托曼读出来的问题

访谈里有一段很坦白的自白。特拉维斯说,他当时读理查德·斯托曼(Richard Stallman)的文章,很欣赏他对知识产权法的思考,也觉得那种集体协作的理想要是真的能成该多好。然后他读到一句话,大意是:不生孩子,就没这个问题。

那一刻他停下了。他当时已经有三个孩子,后来一共六个。他意识到自己没法上这趟车。

他给自己定了一个方向:一定有办法既保留开源的文化,又能赚到足够养孩子的钱。

这个念头把他推向了一个自己完全陌生的领域:经济学。他在博士期间一边读资本论,一边读亚当·斯密,一边读米塞斯。他说自己对教育体系有一种羞愧感:他高中是毕业致辞代表,大学成绩也很好,一路都是好学生,可关于钱是怎么运作的、价格是怎么形成的,他一无所知。

他读到米塞斯 1920 年那篇讨论社会主义共同体经济计算问题的论文。核心论点很简单:没有私有产权,就不会有真实的价格;没有价格,官僚就无法知道资源该怎么分配。价格体系是在人们自由交换的过程中涌现出来的,而这些交换的前提是交换者对交换物有处置权。这套信息流,自上而下的体制拿不到。

特拉维斯说,这个论证让他豁然开朗。价格其实是一种信号机制,和开源社区里那种“谁能解决问题谁就有影响力”的机制是同构的。区别只是前者用货币计价,后者用声誉和贡献计价。

写书、定价、赚够 25 万就免费

2005 年前后,特拉维斯面对一个很现实的问题:他在写 NumPy,但手上没有经费,还养着一个研究生。他不想按学术界的常规套路去跑经费申请。他做了一个实验。

他写了一本书,叫《NumPy 指南》(Guide to NumPy),自己出版、自己定价、自己用 PayPal 收款。他设置了一个规则:如果他靠这本书赚到 25 万美元,或者过了五年,他就把书免费开放。

结果是:三年卖了 3 万册,赚了 9 万美元。他用这笔钱付了自己研究生的工资,一年两万五。

他收到过大概五封骂他的邮件,说他靠开源的东西赚钱是坏人。更多人表示理解:书是可以收费的,这是围绕开源做商业的一种合理方式。

这个实验有个意外收获。几年后,DARPA 有个项目的负责人叫 Chris White,他记得特拉维斯的名字,正是因为当年读过这本书,觉得这种定价模式很有意思。Anaconda 后来申请 DARPA 经费的时候,本来没有去 demo day,按惯例不可能拿到经费,但因为 White 认识特拉维斯,他们破例拿到了。那 9 万美元的书,最后变成了几百万美元的科研资金。

一年半之后特拉维斯去了 Enthought,有了全职工作,书也就顺势开放了。他说当时有个社区小组正在做 SciPy 的文档,需要书里的内容,他就直接放开了。

莱克斯说,这件事放到今天再做一遍,大概能赚得多得多,因为社交媒体和工具的杠杆完全不一样了。特拉维斯同意。他说他这几年也想过再写一两本书,还试过雇枪手,但都没成。原因也很实在:他被手上正在做的事情吸得太满,静不下心。

离开学术,加入 Enthought,然后是 Anaconda

2006、2007 年,特拉维斯的终身教职评审回来了。结果是把系里劈成了两半。有人是他的铁杆支持者,有人坚决反对。事情一路闹到大学校长那里,最后的结论是:过两年再来一次。

他当时的反应是:算了吧。

他打了个电话给艾瑞克·琼斯,说想过来一起做事。2007 年他搬到了德州。他在 Enthought 待了四年,琼斯让他当了总裁。他们一起帮琼斯买下了合伙人的股份。这件事最后没有善终,他和琼斯今天不再是朋友。特拉维斯说他仍然尊重琼斯,仍然觉得遗憾,但那件事有两面,他不想细谈。

2012 年,他和彼得·王(Peter Wang)创办了 Continuum Analytics,2015 年改名 Anaconda。这个名字今天几乎是 Python 数据科学的同义词。

Anaconda 最初的两个技术方向是:让数据科学能真正扩展到大规模机器上;确保 Web 这波浪潮不会绕过 Python 社区。现实推着他们走了一条更务实也更急迫的路。

真正让他们动起来的是一个具体的痛点。2012 年,谷歌园区有一次聚会,圭多也在。会上他们直接跟圭多说:Python 的打包问题必须解决,用户拿不到东西。圭多的回答是:你们自己去解决,我不觉得我们会去做。

圭多确实从来不太关心打包。特拉维斯说他问过圭多这个问题,圭多的回答是:我不用它,我不装新库。作为语言的创造者,这个立场也算合理,你需要什么就直接放进发行版里。

可普通人碰到的就是这道坎。特拉维斯举了一个让他印象很深的例子。当时有人说,别改 NumPy 了,我花了一周才把 Python 环境装好,你一改我就得全部重装。

那一刻他意识到问题有多严重:一个库不敢改,原因不在技术上有什么顾虑,而在安装这个环节太痛苦,用户不允许。

Conda 要解决的不只是 Python

Conda 的设计决策其实很激进。他们决定做一个跨操作系统的包管理器,Windows、macOS、Linux 都支持同一套逻辑,而且不局限于 Python,任何语言都能装。

为什么?因为 SciPy 里面全是 Fortran,scikit-learn 的底层链着一堆 C++。Python 原来的包管理方案,尤其是早期,根本不处理二进制依赖这件事。而二进制依赖恰恰是科学计算生态最卡脖子的地方。

Conda 最初最出名的用法,就是一句命令装 scikit-learn。scikit-learn 依赖 SciPy、NumPy 以及底层的 Fortran 和 C++ 二进制,用 pip 装极其痛苦。而从 2013 年到 2017、2018 年,这是安装 scikit-learn 唯一的靠谱方式。

pip 后来通过 wheel 机制补上了很大一块。但 wheel 有个根本性的取舍:它不处理多方依赖,遇到二进制依赖就直接 vendor 进来,把所有东西打进一个包。一旦底层依赖变了,整个 wheel 都得重新构建。所以特拉维斯说,TensorFlow 到今天也不该用 pip 装,用 pip 装 TensorFlow 是个糟糕的主意,但很多人就是这么做,因为文档里这么写的。

这里有个挺荒诞的现象。Conda 早于 pip,而且解决了很多 pip 解决不了的问题。但因为互联网文档的长尾效应,你在网上搜任何“怎么装 TensorFlow”,前十页全是 pip。2016、2017 年的时候 Conda 还会出现在搜索结果里,现在基本消失了。

特拉维斯给出的实用建议是混合用法:用 Conda 搭底层环境,装 NumPy、SciPy、scikit-learn 这些有复杂二进制依赖的大件;然后用 pip 在上面装那些纯 Python 的小工具。一定要小心 pip 装 OpenCV 或 TensorFlow 这类东西,因为它们一旦进来就很难更新。

他还提了一个一直想加但没能加进 pip 的功能:允许 pip 把依赖问题委托给系统包管理器。pip 装东西的时候,遇到某些依赖可以转交给 Conda 或者 RPM 处理。这个功能不复杂,但需要有人去做,而这件事一直缺钱。

Conda 还有一个长期没解决的组织问题。特拉维斯说,任何项目一开始都是公司支持的,哪怕那家公司就是你自己一个人。要真正病毒式传播、成为基础设施,它必须变成社区驱动的。需要有人做治理,需要把外部贡献者拉进来。Conda Forge 社区做到了这一点,Conda 本身一直没能真正社区化。他直言,这是他认为 Anaconda 这家公司应该更推进的方向,这场仗不该由公司来打。

Numba:让“Python 慢”这个理由消失

Python 慢,是一个长期被拿来当笑话讲的点。想快就用 C++,想舒服就用 Python,这是很多人脑子里的二分法。

Numba 想改变这个分法。它的目标是:你写 Python 循环,然后它被编译成机器码。

这个想法其实比特拉维斯写 NumPy 的时候就有了。NumPy 里有个东西叫 vectorize,你传进去一个处理标量的 Python 函数,它会给你一个能作用在数组上的通用函数(ufunc)。听起来很美,实现上是假的:它会遍历数组,每次调用都跳回 Python 解释器,所以极慢。

特拉维斯一直想要一个真正的版本。2009、2010 年,他找人试着用 PyPy 做过,效果不好,太重。2012 年他和彼得刚创办 Anaconda,手里有了钱,他又想起这件事。他当时听朋友戴夫·比斯利(Dave Beasley)讲过 LLVM,突然意识到,这就是那个答案。

于是他写了一版 Numba,核心思路非常直接:把 Python 字节码映射到 LLVM IR,然后让 LLVM 负责生成机器码。

Numba 本身是用 Python 写的。它自己不做解析,也不做代码生成,只负责从 Python 字节码翻译到 LLVM 中间表示。特拉维斯说,这体现了 LLVM 真正的价值。它不好在“虚拟机”这个命名上,好在它是一个大家能协作的公共地表。在 LLVM 之前,你想扩展 GCC 或者 Intel 编译器,那是另一回事;LLVM 出现之后,编译器后端第一次变成了一个大家可以一起搭的公共基础设施。

Numba 被重写过三次。特拉维斯说,这让他第一次理解了为什么计算机科学的课程是那样设计的:编译器真的很难写,它会用到几乎所有核心概念,树结构、类型推导、pass 流水线。写一个能跑的原型很容易,写一个不变成意大利面条的编译器是另一回事。

Numba 的商业模式也很聪明:免费版跑 CPU,商业版 Numba Pro 跑 GPU,它是最早的 CUDA JIT 之一。2012、2013 年你就能写一个 ufunc,跑在 GPU 上,自动并行化,速度提升千倍。大公司愿意为了速度付钱,这是一条靠谱的商业路径。

特拉维斯总结了两类人愿意为开源软件掏钱:一类为好的用户界面付钱,一类为速度付钱。

回到那个 np.sqrt 的争论

访谈里聊到一个具体的技术八卦。安德烈·卡帕西(Andrej Karpathy)在特斯拉带自动驾驶团队,他在推特上说,把数据加载里的 np.sqrt 换成 math.sqrt 得到了显著的性能提升。然后有人回帖说,用 Python 原生的平方根(也就是幂运算 0.5)还能更快。

特拉维斯对这件事的解释很清晰。如果你对一个标量调用 NumPy 的数学函数,它一定比 Python 原生的慢。原因是 NumPy 的数学函数是通用函数,它要处理的不只是标量,还要处理任意维度数组,所以即使是标量,它也要先广播成零维数组,然后走完整个通用函数的机器,最后再把结果解包出来。

作为对比,Python 运行时里那个平方根就是纯 C 调用,因为它只需要处理标量这一种情况,可以做大量特化优化。

那什么时候该用数组?特拉维斯给了一个粗略的经验值:数据量低于一千,甚至一万左右,Python 原生方案可能更快;超过一万,你就该用数组了。因为数组带来的并行、缓存局部性、SIMD 优化都会开始起作用。

他还补了一句更重要的判断:别在这上面过度纠结。他对“过早优化”这个说法是认同的。你为了优化某个具体调用省下的那点时间,可能远不如你为了优化它而牺牲的代码清晰度来得重要。

Python 2 到 3:一场关于惯性的课

Python 3 从 2008 年发布,到 2020 年 Python 2 正式停止支持,中间拖了十二年。这个跨度里有很多教训。

特拉维斯的判断很直白:Python 3.0 到 3.2 其实没什么值得升级的东西。在他看来,Python 3.3 才算真正的 Python 3.0,因为那时候才凑够了一波让人愿意动的特性。3.4 开始有人想要,3.5 加入了矩阵乘法运算符 @,那时候变成了不得不升级。

更深层的问题在共情上。

一个用户为什么要从 Python 2 升到 Python 3?除了修复了一些圭多自己看不惯的东西(比如 print 从语句变成函数),早期版本里几乎没有什么让用户感到“值得付这个迁移成本”的东西。你没法单纯靠“这是新版本”就让一个庞大的人群搬家。惯性一旦形成,改变它就非常困难。

特拉维斯还提到了一个容易被忽略的现实:Python 一直没什么钱。圭多有一份工作,但那份工作不是全职做 Python。整个语言生态的核心,几十年来都建立在志愿者劳动之上。这既是它的力量,也是它的风险。

莱克斯顺着说了一句挺有意思的话:也许这是一种特征,不是缺陷。就像奥运会运动员常年资金不足,恰恰是这种匮乏激发了伟大。

特拉维斯没有完全同意,也没有完全反对。他说这确实可能是部分原因,但他不认为钱是坏事。他也不认为有钱和保持价值观互斥。

Quansight、Labs、OpenTeams:给开源装一台经济引擎

2018 年,特拉维斯离开 Anaconda,创办了 Quansight。它的官方使命是“把数据连接到开放经济”,本质上是一家数据科学全栈咨询公司。

真正的创新在里面。Quansight 下面有三个不同性质的东西。

第一是咨询服务,做正常的生意,帮企业搭数据基础设施、训练团队、解决使用 Dask 和 GPU 的具体问题。

第二是 Quansight Labs,一个非营利性质的机构,专门养开源项目的维护者。它的模式叫“社区工单”:企业可以付钱让某个人去改进 Spyder、NumPy 或者 SciPy,但不能指定具体做什么,只能提出自己的优先级和期望。实际的开发方向由维护者和社区共同决定。Quansight 每个项目的利润都会有一部分直接流向 Labs。

第三是 OpenTeams,这是特拉维斯最兴奋的部分。他的判断是:企业在软件上遇到的问题,很大一部分不在买不到工具,而在买到的工具都需要大量定制,而定制要靠昂贵的咨询公司。开源方案加上同样的定制,成本可以低得多。OpenTeams 想做的就是把这件事变成一个网络:一头连企业客户,一头连像 Quansight 这样的服务商,中间做销售、质量控制和交付保证。

他还想在开源和风投之间搭一座桥。他创办了一个叫 Quansight Initiate 的基金,一部分 carry 直接流向 Labs。他的想法是:每一个风投基金都该配一个开源研究实验室,因为投资人对早期项目有天生的信息优势,而实验室对社区有天生的信息优势,两者结合可以形成正循环。

这套东西听起来很理想化,但特拉维斯已经做过一轮了。他对 Anaconda 时期的评价是:那时候创新太多,本来可以拆出五六家公司,因为结构没搭好,都留在了内部。Dask 现在衍生出了两家公司,Bokeh 也可以是一家,JupyterLab 又衍生出好几家。他想做的就是把这种自发的生态涌现,变成一套可以复制的孵化机制。

钱花在哪里,是一个巨大的错配

访谈里有一段沉重的对比。911 二十周年的时候,特拉维斯算了一笔账:美国在中东各项行动里花了超过六万亿美元。他说,把政治放一边,光是想一想,把这笔钱里的一小部分分给那些写“让世界运转的工具”的程序员,会带来多大的影响。

他说这就是他二十年来一直在试图解决的问题。为什么支撑全球科学和工业的基础设施,是由志愿者在业余时间维护的?为什么一个能影响几千万人的库,它的作者从来拿不到工资?

莱克斯接了一句很有画面感的话:一起做贸易的国家不会打仗,也许一起写代码的国家也不会。特拉维斯说这个比喻好,因为开源本身就是跨国界的,没有国界概念。而商业是有辖区、有法律、有边界的。这两者之间的摩擦,是他每天在处理的实际问题。

他谈到在伊朗、巴基斯坦做过线上会议,遇到过程序员和科学家。他说这种接触有一种人性化的力量。我们平时接触到的关于其他国家的认知,大多是浮在表面的段子,而那些段子和真实的普通人之间,差距非常大。

他说如果未来大国之间真的会发生冲突,最可能的形式是网络战。而这三个国家都有一流的开发者。如果这些开发者之间有大量协作,那可能就是一层防止冲突升级的底层基础设施。

一款语言,一款编辑器,一张桌子

聊到工作环境,特拉维斯说自己是 Emacs 派,从写 SciPy 和 NumPy 开始就是 Emacs 加 Linux。SciPy 和 NumPy 全部是在 Emacs 里、在 Linux 机器上敲出来的,版本控制先是 CVS 再是 SVN,Git 是后来的事。

他开玩笑说,有一次他发推特说 Emacs 比 Vim 好,被骂惨了,最后把推特删了。他不打算再碰这种话题。日常临时编辑他还是会用 Vim。

关于 Lisp,他承认自己年轻时完全看不懂,觉得那些括号在阻挡思考。现在回头看,他越来越理解 Lisp 的价值,也开始欣赏 Clojure 和 Hy 这类把 Lisp 思路带到别处去的尝试。

他也明确说,Lisp 很难真正进入大众。因为世界上大部分是“偶尔编程的人”。他们不打算以编程为职业,也不想面对各种精巧的语言设计,他们只想把手里的事搞定。这就是为什么 BASIC 当年如此成功,也是为什么微软当年应该把 BASIC 那套思路在十年前就换成 Python。

他自己不用多屏幕。他说他在脑子里习惯快速切换,物理屏幕太多反而分心。只有在一头看数据、一头要敲东西的时候,第二块屏幕才真的有帮助。

至于操作系统,他用了十年的 MacBook。原因非常不极客:他经常要做演示、插投影仪,Linux 桌面上那些驱动折腾真的折腾不动了。

怎么找到对的人

关于招聘,特拉维斯给的第一个建议很直接:先去做一个开源项目,让人们在上面贡献,然后从贡献者里招人。

他说传统的面试很难。简历是候选人想让你看到的版本,不是真实。一两个小时的面试他判断不出来,除非他跟你一起工作过几周。

他更在意一种更底层的东西:一个人是否从根本上承认自己需要学习。他把“学习”当成公司里最重要的价值观之一。如果一个人觉得自己什么都懂了,他会走得很辛苦。谦卑是学习的第一步。

在他自己的经历里,最常见的一种感受就是:他懂的东西越来越多,同时也越来越意识到自己懂的这点东西在整个图景里只是很小的一部分。所以他倾向于多听,少说。他还提到妻子会提醒他,他现在有时太急于把自己的想法说出来,要让别人也有时间说话。

什么样的程序员能做得好

聊到这个问题,特拉维斯给了一组相当诚恳的回答。

第一是好奇。没有好奇心的程序员会变得平庸,会失去动力,最后做不出真正好的东西。

第二是别想一次做完全部。人的精力有限,每个人擅长的方向不同。最有效的方法永远是缩小问题:定义清楚输入、定义清楚输出、写下来你到底想做什么,哪怕只是写几行字。很多人一上来就接一个大项目,想一口气做完,最后迷失在里面,做得一塌糊涂。

第三是善用别人的工作。别觉得从 Stack Overflow 上抄代码有什么问题。没有人是从零开始做所有事的。但要抄得明白。你可以复制粘贴,但要理解它为什么这么写。这就是好奇心起作用的地方。盲目复制粘贴,你永远学不到东西。

第四是对技术炒作周期保持警惕。每隔几年就会有人说,测试驱动开发是答案,或者面向对象是答案,或者敏捷是答案。这些口号里通常有一点点真东西,值得你学。它几乎肯定不是所有问题的答案。

怎样才能做出下一个 SciPy

特拉维斯给的答案很短,而且都是动词。

听。听谁?听有需求的人,也就是所有人。听很多人。

然后做。你会摔倒,第一版很可能做得很烂,这没关系。迭代才是创新的核心。真正卡住很多人的是那种心理上的犹豫:我们知道它现在很差,下一次会更好。态度本身才是过不过得去这道坎的关键。

也别指望这东西像刷短视频一样能带来即时快感。好的东西不会那样出来。你得习惯深度问题,习惯在那里坐一两个小时不动。

莱克斯问他,写 SciPy 和 NumPy 的时候孤独吗?特拉维斯说,非常孤独。你必须有内在的驱动力。那个内在驱动有一个具体来源:他必须首先相信这件事在某种角度上是对的,是这个世界需要的,然后才能一直做下去。SciPy 对他来说就是这样的东西:Python 已经有一批有影响力的人了,它需要更多库,这件事本身是好的,那就去做。

有一个数字他特别强调:一个新的开源项目,至少要给它 18 个月,才能真正知道反馈够不够。六个月是不够的。即使你做的东西完全正确,六个月后它也只是刚刚浮现出来。你在跟人打交道,而人有惯性,不会改变得那么快。

作为一个导师,被差评是什么滋味

访谈里有一段他谈教学的段落,值得单独拎出来。

特拉维斯在杨百翰大学教过很多课,信号处理、概率论、电磁学。他很想当一个好老师。他的学生给他打过差评,理由是他讲得太高深了。

他反思说,他的问题在于把学生的现有水平估计得太高,以为他们已经懂了,于是就直接往下讲。他心里是尊重学生的,甚至可能正是因为太尊重,所以一上来就假设对方跟他站在同一水平线上。

他讲了一个具体的细节。第一年教书,他准备了一堆投影仪透明胶片,一进教室就打得很兴奋地一路讲过去。两周后做小测验,全班什么都不会。

他意识到问题,把胶片收起来,转身开始用黑板。黑板逼他慢下来。写字的速度限制了讲课速度,学生终于有时间消化、有时间思考。他自己也因此变得专注。他的板书不好看,可他爱上了那个版本的自己。

这件事后来影响了 SciPy 的设计理念。他们一直知道 SciPy 有一个教学层面的属性:要把知识递到人手里,而不是炫耀自己懂多少。名字不能太长,函数要直观,文档要能真正把人送进细节里去。

一个人的显卡,两派人的战争

聊到今天的生态,特拉维斯提到一个让他很在意的事:GPU。他指出,NumPy 本该直接在 GPU 上跑。今天你需要装一个完全独立的库叫 CuPy,这本身就是历史遗留问题。从原理上讲,数组没有任何理由不能在 GPU 上工作。原因全都出在 NumPy 当年那个类型系统的设计,让扩展变得过于困难。

他反省说,如果当年 dtype 的实现能更贴近 Python 3 的类型系统,后来的发展会顺很多。Numba 能更好地接入,渐进类型会更容易,GPU 支持会平滑得多。他说当年他只有 4 个月写第一版,对 Python 2 的类型元编程机制理解还不够深,这个教训他记到今天。

现在他正在推一个叫 data-apis.org 的项目。它不做统一数组,它做统一接口。不同的库可以有各自的实现,但至少对外暴露一套统一的 API。他说现在数组社区的分裂比 2005 年更严重,因为牵扯到的人和钱都多得多了。行业当然养得起多套技术栈,但效率损耗也是真实的。他倾向于让实现层竞争,让接口层协作。

他也去 Google 和 Facebook 分别谈过。他讲得挺坦率:TensorFlow 和 PyTorch 都晚了,两家本来都是 C++ 库,等到发现必须支持 Python 时,仓促加了一层外挂。他说 TensorFlow 早期那个 NumPy 接口是他见过最难看的 API 之一。他当时在一个会议上直接跟演讲人说,这个 API 以后不要在 PyData 大会上再展示了。他承认自己平时批评太少,这是他难得发飙的一次。

两家后来走的路也不同。Facebook 更愿意让社区参与库本身的结构,Google 则更倾向于让社区用,但不容易让社区改。这背后还是那个老问题:你要照顾的是给钱的少数大客户,还是给你带来活力的多数黑客?特拉维斯倾向后者,但他也承认这个平衡很微妙。太偏向黑客会破产。

每次填空白的时候,你填的是善意还是恶意

访谈里有一段他讲得相当动情。他说自己后来意识到,2012 年那封离开 NumPy 的公开信,对他是很艰难的。做任何有影响的事,你都不可能被所有人喜欢。你会被质疑,被批评,有些批评是需要的,因为他不可能什么都做对。

他真正在意的是另一件事。当信息不完整时,人倾向于用最坏的假设去填补空白。他会收到一些批评,那些批评基于的是对他的动机最恶意的解读。他心里会想,这个结论是从哪来的?我愿意接受批评,可你完全误解了整件事。

他说他宁愿先假设对方是善意的。当你对别人投射善意时,对方也往往会真的向你靠近。这种机制有病毒式的扩散效应。

莱克斯补充说,推特和 Facebook 早年就发现了负面情绪能带来更高的参与度,所以整个系统在结构上是鼓励负面的。这场对抗,我们一直在打。

特拉维斯同意。负面更容易,太容易了。而且我们的脑子天生对负面更敏感,可能是一种防御机制,怕不赶紧躲开就会被吃掉。

但他说,一个软件工程团队要是真想把事做成,就必须全员练习投射善意。这不是道德要求,它就是工程要求。

微软当年如果出价出对了

特拉维斯透露了一个细节。2014 年,Anaconda 收到过微软的收购意向。当时微软刚买了一家 R 语言公司,也想在 Python 这边补一块。但那个出价来自离萨提亚·纳德拉(Satya Nadella)还差两级的人。如果那次出价是 Scott Guthrie 亲自发起的,特拉维斯说,他今天大概率就在微软了。

他说这话的时候没有遗憾的意思,更多是一种对历史分叉点的观察。他对微软后来在开源上的转变评价很高。微软的文化里有一件一直做得很对的事:他们知道自己的权力来自开发者。当年鲍尔默那句关于开发者的名言在网络上被嘲笑,但那份底层认识是真的。收购 GitHub 在他看来是个聪明的动作,表明他们知道开发者在哪。

可他也指出,微软对 GitHub 的整合还没有形成像 OpenTeams 那样的拓扑结构。对开发者表达支持和真的让开发者赚到钱,是两件事。

他顺便讲了一个关于企业营销的观察。他见过很多公司想用钱来跟开发者群体建立关系,但方式很拙劣。他的建议是:直接把钱给最受欢迎的开源项目。这是成本最低、效果最好的营销投入之一。把一个百万美元的营销预算里的一小块,直接投给 NumPy、SciPy、Jupyter,换来的是开发者的好感和长期的信任。他希望企业把这件事理解成营销,而不是慈善。这笔账在 ROI 报表上说得清。

给高中的一个建议,和给世界的一个希望

访谈的最后,莱克斯问了一个比较笼统的问题:对今天的高中生和大学生,有什么建议。

特拉维斯的回答分两层。

第一层是家庭。他说,找到你爱的人,然后承诺于他们。家庭就是你爱的、并且承诺的人,这个概念比血缘更广。它给你一种任何别的东西都给不了的锚。

第二层是别急着下定论。他说他看到很多高中生已经觉得自己什么都懂了,他自己当年也这样。这可能是自然的。可你真的要给自己十年的时间去看世界,因为你会变,你现在的立场很可能不是你十年后的立场。

他说他年轻时候非常自由意志主义,特别讨厌美联储,现在他的观点变得更有层次了。这本身就是成长的一部分。

他给出的最核心的一条建议,是建设,而不是摧毁。

他说,人的本能是遇到不喜欢的东西就去攻击它。更好的方式是:造一个新东西去替代它,把人吸引到你的道路上来。你不需要摧毁一样东西才能建立另一样东西。

而这一切,最终都落在好奇心、爱和那些具体的事情上。不要只跟着钱走。找到你相信是对的东西,然后一直做下去。给这件事足够长的时间。

访谈的结尾,莱克斯留了一句在编程圈流传的玩笑,叫霍奇森定律:任何足够先进的 Lisp 应用,最终都会用 Python 重新实现一遍。

如果你现在就想动手

从特拉维斯这几十年的经历里,能挑出一些今天就能开始做的事。

  • 从一个真实的、你自己的小痛点开始写工具。SciPy 的起点是他做 MRI 研究时发现自己缺一个 ODE 求解器。从一个具体的不方便开始,别从宏大的愿景开始。
  • 给项目至少 18 个月的耐心。六个月看不出反馈很正常。如果你觉得方向对,先撑一年半,别在第六个月就下结论。
  • 写代码前,先写下你要解决什么。定义清楚输入和输出,哪怕只有几行。大部分失败的项目不是写不出来,是没想清楚要写什么。
  • 复制粘贴,但要理解。Stack Overflow 和自动补全工具是起点,不是终点。每次抄完,问自己一遍这段在干什么。
  • 默认对方是善意的。尤其在信息不完整的时候。这么做能真让团队跑起来,这是工程实践,不是天真。
  • 如果你在管技术团队,先做一个开源项目,再从贡献者里招人。简历和一两小时面试看人,远不如一起工作两周准。
  • 如果你是公司决策者,把营销预算的一小块直接投给支撑你业务的顶级开源项目。回报率远比你想的高,而且能长期生效。
  • 搭 Python 环境时用 Conda 打底,pip 补细节。科学计算那一层依赖复杂二进制,别用 pip 硬装 TensorFlow、OpenCV 这类东西。
  • 不要为了优化一个具体调用纠结太久。先让代码清楚、可读,再做优化。NumPy 对标量慢,这是设计取舍,不是 bug。
  • 做建设者,不做摧毁者。遇到你不喜欢的东西,造一个更好的替代它,而不是在评论区里骂它。特拉维斯这四十年做的每一件事,本质上都是这样。

本集资源与出处