Why Python?
By Eric Raymond on Mon, 2000-05-01 01:00. Software
在获得这篇自述之前,Cardinal Biggles
让Eric坐在舒适的椅子上思考了4个多小时...
我首次注意Python是偶然的,在那时我并不太喜欢我所看到的Python。1997年初,Mark
Lutz 的书 <<Programming Python>> 刚由O'Reilly & Associates出版。在O'Reilly
由
一些不可思秘的赞助者经过一些我不想去理解的选择过程之后,新书会不时被送给我。其中一本就是<<Programming
Python>>。由于我喜欢收集计算机语言,我发现Python比较有趣。我知晓数十种通用语言,把写编译器和解释器当作乐趣,也曾自己设
计过许多种专用语言和标记形式语言。在我写这篇文章时我刚完成的一个项目是一个用来操作PNG(Portable Network
Graphic:便携式网络图像)图像的叫做SNG的专用语言。感兴趣的读者可以访问SNG的主页http://www.catb.org/~esr/sng。在我的Retrocomputing
Museum网页(http://www.catb.org/retro.)上有我写过的一些罕见的通用语言的实现。
我
听说过的关于Python的足够多的了解是仅仅知道它是一种所谓的"脚本语言",一种内建内存管理和容易调用其它语言代码和与其进行协同操作机制的解释型
语言。于是,头脑中带着一个最迫切的问题:"Python什么地方比Perl更有成效?"我深入研究了<<Programming
Python>>。
众所周知,Perl是现代脚本语言的巨无霸。它在很大程度上取代了shell成为用于系统管理的脚本语言的选择。
这
部分归功于它有广泛的对于Unix库和系统调用的工具集,部分归功于活跃的Perl社区构建的巨大的Perl模块的汇集。这个语言通常被认为是网上使用
CGI语言的地方它占85%。它的创建者Larry Wall理所当然地被公认为开源社区最重要的领导人之一,在当前黑客名人录上仅次于Linus
Torvalds和Richard Stallman。
那时候,我已经在一些小型项目上使用过Perl。我发现这个语言非常
强大,即使它的语法和其它一些方面显得过于特别,而且一不小心的话容易带来麻烦。看来,Python成长为类似的脚本语言似乎还有不小的困难需要克服,
当我阅读这本书时,我首先注意了它和Perl的区别。
我在首个Python的新奇的大家都注意得到的特性上犯了差错:事实是
空白符(缩进)在这个语言的语法里实际上是有重要的意义的。这个语言没有类似于C和Perl中使用括号的语法。取而代之的是,缩进的变化能够界定语句
块。并且,和大多数黑客最初了解这一特性时一样,我在自发的厌恶中退却了。
作为一个老一代的程序员,我曾经在20世纪70年
代有数月在批处理FORTRAN下编写过程序。虽然大部分的黑客并没有经历那个时代,但我们的文化中还是保留了一些对于那些丑陋的固定领域语言的记忆。
事实上,那时被用来形容在Pascal和C中面向标记语法的新风格-自由格式(Free
Format)早已被遗忘,早在十年以前,几乎所有的语言就被以这种方式设计。总而言之,对于Python的这一特性,很难责怪任何一个人,尽管最初我们
的反应就好象走入一堆恐龙粪中一样。
那当然是我个人的感受。我没有多大兴趣地浏览了对这个语言介绍的其余的部分。除了那些比起Perl十分简洁的语法和很容易地做出按键和菜单等GUI元素的
外,我并没看出其它推荐Python的理由。
我把书放回了书架,在心里上我记下了:我以后或许会用Python做一些以GUI为中心的小型项目-仅仅为了确定我确实知晓了这门语言。而那时我并不相信我在python上的所见到的东西能与Perl有力地竞争。
几
个月来许多其他的事情让我把那件事抛在了脑后。1997年剩下的时间对我而言具有重大意义,正是这一年我创作并发表了<<大教堂与市集〉〉。
但我也抽时间写了一些Perl程序,其中两个也具有一定规模和复杂度。其中之一是keeper,
一个现在还被用来在Metalab的软件归档中对提交的进来的文件进行归档。你在Http://metalab.unc.edu/pub/Linux/!INDEX.html中看到的网页就是由他生成的。另一个,anthologize,则被用来为Linux文档工程的HOWTOs的Linux第六版自动生成PostScript。这两个程序都能在Metalab得到。
写
这些程序导致我渐渐对Perl产生了不满。大一些规模的项目似乎把Perl的烦厌之处放大成了严重的,持续出现的问题。在100行中看起来古怪的语法象附
近围起来的篱笆上的1000个荆棘。"一项任务多种实现(译者:简写为"TMTOWTDI",Perl官方的座右铭,在Perl
编程中是深受鼓励的)"在小规模上提供了趣味和表现力。但在跨越一个大的代码基底中,使得它相当难地去保持程序风格的始终如一。很多后来以补丁形式加进去
的特色用以处理控制大的程序复杂性(对象,作用范围,use strict指令等)有一种脆弱的,草率的拼凑的感觉。
这些
问题合并在一起造成大体积的Perl代码仅在几天之后变得过度难的以阅读及领会为一个整体。我发现我花费越来越多的时间纠缠于语言造成的东西之中而不是我
的应用程序中的问题。要命的是,写成的代码是难看的。难看的程序看起来象难看的吊桥:他们比漂亮的更加容易倒塌,因为人类领会美是与我们处理和了解复杂
性的能力有密切的关系的。一门语言难以写优雅的代码必定难以写出优秀的代码。
凭着对大量的语言的基本的了解,我可以觉察全部的语言设计已经走向它的功能的边缘。到了1997中期,我开始认为"必须有更好方法",开始投向更为优雅的脚本语言。
我
没有认为是要回到C作为当然的选择。在一个新程序自己进行内存管理,象内核改动,科学计算和3-D图形,在这些地方你必须独立的使得速度最大和紧紧控制内
存的使用,这种情况下做出一个有意义程序相当漫长。因为你需要考虑硬件。
大多数的其它的形势,接受调试器缓出溢出,指针-别名的用...进行交换问题,malloc/free
memory 内存泄漏诸如此类其它相关难以处理的在今天机器是导致的问题多多。好得多是用一些机器时间周期和内存进行交换,脚本语言内存管理节省了贵重的人类时间.甚至,自从-1990中期以来,这种策略优点精确的驱动Perl迅速增长。
我
摆弄过Tcl
。很快就发现它比Perl更差,我是老的LISP爱好者,我关注现时各种多样的Lisp和Scheme语言,由于通常的历史的原因,许多聪明的设计由于缺
乏或者不存在的文档几乎是无用的,不完善的对POSIX
/UNIX设备存取,一些小而无组织用户社区。Perl的流行不是偶然;多数竞争者或是在大型项目劣于Perl或者不知何故远不及象他们的高级设计那样使
得它们有用.
我再次研究Python几乎象我第一次一样意外。1997,十月一系列怀疑在fetchmail-朋友邮寄列
表文件宣告最终用户是已经越来越多的麻烦生成配置文件使用我的fetchmail程序.当一用户有POP3和IMAP帐目在多个站点,那文件使用简单的
经典的UNIX自由-形式语法,,就变成可怕的复杂.看一例子,Listing 1是我的简化了的版本.
Listing 1
我决定写一个用户友好地编辑器-fetchm一ilconf以着手处理那问题。设计fetchmailconf的目标是清楚的:以流行的,人与机械控制的正确的图形用户界面界面带着选择按钮,滑动按钮和各种表单来完全的隐藏控制文件语法。
在perl中实现这个工具的想法并没有让我发抖。我看到过Perl中图形用户界面代码,它是混合有Perl和tcl,甚至那看起来比我自己纯粹-Perl代码还要难看。正是在这一刻我记起了正是在六个月前我对自己的约定。我得以体验python时机到了。
当然,这些给我带来一次面对面python对我的严格考验,空白符的重要性。然而这次我充满感情地写了一些图形用户界面元素代码。说也奇怪,大约二十分钟使用Python的空白符不自然的感情没有了。我只是缩进了代码,总之如同我在C里面做的一样,它运行了。
这
是我第一次感到吃惊。我第二次吃惊是在这个项目上几个小时后。我注意到(在Python编程中允许必要的停顿以便了解一些新的特性),我产生代码几乎是与
我敲入代码的速度一样快的。当我认识到这一点,我是相当吃惊。事实上,当你写下的代码不能完全表达你问题在你心中的想法时,不得不回头去看你的那些代码
实际上与你当时的想法的区别,这样的次数是编代码时努力程度的重要的量度标准.当你增进这门语言的经验时,好的语言设计重要的标准之一是这种情况下所犯
的错误在百分比上减少的速度是多么快速。
当你写代码时几乎与你打字的速度一样快时,你的错误率靠近零时,表明你大体上精通了这门语言。那并没有什么了不起的,因为它仍是刚开始时的那样,我有规律的停下来查找新的语言及库的特色。
我是在与一个设计得异常优秀的语言打交道,这是我第一次在python上体验到。当你学习时,当你的错误率下降近乎零时以前,大多数的语言在它们的设计中有那么多摩擦和别扭的地方。Python是我所使用过通用编程语言中第一个逆转这种过程的.
我
掌握语言特点时并没有花费我多少时间,我写了一个带图形用户界面可运行且有用的fetchmailconf,6天中的两天我在学习Python
。这反映另一个有用的特性:它是简洁的,你要可以随时在你的头脑中把握住它的整个特性(至少对它的库也可以有一个观念索引)。C是一著名的简洁的语言。
Perl众人皆知它不是,它的一个口号是:一种任务多种实现。结果便是Perl不可能是简洁的。
但我最戏剧性的发现摆在了我的面前。我的设计有问题:可以容易地从用户的图形用户界面的行为中。但是编辑产生的配置文件非常艰难的问题。把它们转为一种可
读的形式就更是一个问题。
分析cfetchmail程序的配置文件语法程序是相当复杂的。它其实是用YACC
和Lex写成的-两个经典的UNIX工具,用于生成分析语言的C代码。为了使fetchmailconf程序能编辑现有配置文件,我想在python中必
须复现那复杂的分析程序。我是很不情愿做这些。部分因为工作量大,部分因为我不能确定该如何使得在不同的语言中二分析程序能接受同样的数据。最后我需要
额外的努力使得随着配置语言的发展同步分析程序。
这问题暂时把我难住了。后来我有了灵感:我让fetchmailconf
程序使用fetchmail的自己的分析程序。我为fetchmail程序增加了configdump选项。把Python写成的初始化程序的形式转储
结果到标准输出。结果大致的如列表2(为了节省空间,有些数据与例子与没有关系的被省略).
Listing 2
Python能因此计算fetchmail--configdump的输出配置,把fetchmail变量的值作为可用的配置。
这
不是成功的最后一步。我真正想要的不仅仅是fetchmailconf利用现存的配置,而是要转变并连接到树形对象。在这树形对象有三个种类的对象:配置
(最上面-对象表示整个配置),站点(表示一个站点被轮询)和用户(表示用户数据附着站点).例子文件描述五个站点对象,其中每个有一个用户对象附着于
它。
我考虑将写成的代码明白知道所有当中三个分类的结构,并使用这一点经过那初始化程序创造相配对
象,但是最后拒绝那想法,因为新类成员可能是随着那生成那配置的语言有新的特色而变化。如果我写以那明显的方法创造对象代码,当类定义或者初始化程序结
构改变时,它将是脆弱,并不再同步。
我实在想的代码能够分析初始化程序的形式和成员,通过自己查询类成员的定义,使自己适应并配合那两种设置。
这
就是称作是元类的解决方法,通常有被认为是相当深奥的-令人难以捉摸。大多数的面象对象语言根本没有支持它。这一点上Perl也一样,它趋向复杂和脆弱。
迄今为止Python低摩擦系数给了我很深的印象。这里是一个真的测试。用其它语言要做到这一点是多么的艰难。我从早先的经验知道那一回合是痛苦的。但是
我深入了到那书本中,研读python元类机制。最后的功能在列表3,调用代码在列表4。
Listing 3
Listing 4
那
技巧看起来不太糟糕吧,是吧?注释也算在内,32行代码。仅仅从我对类的结构的知晓,调用代码是易读的。但代码的尺寸并不是真的很小。振作精神:这代码只
用了我大约九十分钟写的,在我第一次时我就能运行,并能工作得很好。
一再地说我被震惊,肯定是一种保守的陈述。单纯的技术工作在第一次就能工作得这么好,这就很足够了.从起步时用6天我首先在一种新语言中用元类来有创意的
解决,即使我们确定我是有相当才能的电脑黑客,这也是一令人惊异的python的透明和优雅的设计的证明.
在Perl上我决不可能做到如此漂亮的工作,即使我在它上面有丰富的编程经验。正是在这一点时,我认识到我大概会把Perl放在后面。
这是我在python上戏剧性的瞬间。但是,毕竟,它只是巧妙的有创意的解决。一门语言的长期的有用性不仅表现在巧妙的的有创意的解决,而且表现在是多么有效和不唐突上.日常编程工作不仅包含写新程序,更多的是阅读和修改现有的程序。
因
此上述故事的关键是这些:在写下fetchmailconf程序几个星期甚至几个月之后,我仍可以阅读fetchmailconf代码和不需多少脑力便轻
松理解它。除了微小的工程外,我不会再写Perl代码了,真实的是我再也不用perl写大量的大块大块代码了。我恐惧不得不修改以保持或者再一次编纂的
情景,--但是我根本没有对fetchmailconf产生疑虑。
Perl仍然有它的用处。为微小的工程(100行或者更
少)汲及大量文本进行模式匹配,我是更可能调整基于Perl-regexp的解决方案而不是使用python。好的例子是,看在fetchmail程序
中timeseries和growthplot脚本。事实上,在它有能力存取存取系统api前。这些很似Perl以一种awk/sed /grep
/sh联合的角色所做的那样。对于任何在大型的或更加复杂,我更愿意使用Python精致的特性,我想你也会。