《Why Python?》 -- Eric Raymond http://www.linuxjournal.com/articles/lj/0073/3882/3882aa.png ::-- ZoomQuiet [2006-07-21 07:18:52]

CPUG联盟::

CPUG::门户plone

BPUG

SPUG

ZPUG

SpreadPython Python宣传

1. 中英文版

【以下翻译由Chaox完成,【括号】内的文字为译者注,此同。水平有限,还需要大家帮忙完成一些比较难的句子。 】

Cardinal Biggles had Eric in the comfy chair for over four hours before wringing this confession from him...

Cardinal Biggles让Eric舒舒服服地度过了四个多小时后,“逼着”他写下了这份感悟。【若要取之,必先予之】

My first look at Python was an accident, and I didn't much like what I saw at the time. It was early 1997, and Mark Lutz's book Programming Python from O'Reilly & Associates had recently come out. O'Reilly books occasionally land on my doorstep, selected from among the new releases by some mysterious benefactor inside the organization using a random process I've given up trying to understand.

我第一次看到Python是一次很偶然的机会,而且那时我并不怎么喜欢它。大概在1997年初,Mark Lutz的书《Programming Python》刚从O’Reilly & Associates出炉。O’Reilly的书偶尔会被放在我家的门阶上,它们是由组织里一些神秘的捐赠者从一堆新书中选出来的,我都懒得去考虑他们是怎么选的。

One of them was Programming Python. I found this somewhat interesting, as I collect computer languages. I know over two dozen general-purpose languages, write compilers and interpreters for fun, and have designed any number of special-purpose languages and markup formalisms myself. My most recently completed project, as I write this, is a special-purpose language called SNG for manipulating PNG (Portable Network Graphics) images. Interested readers can surf to the SNG home page at http://www.catb.org/~esr/sng/. I have also written implementations of several odd general-purpose languages on my Retrocomputing Museum page, http://www.catb.org/retro/.

其中有一本就是《Programming Python》,我对它有那么点兴趣,因为我总是收集计算机语言。我知道超过二十种通用的语言,出于兴趣写些编译器和解释器,还设计了不少专用语言和标记性的。我最近完成的项目是一种用来处理PNG图像的称为SNG的专用语言。感兴趣的读者可以从SNG的主页http://www.catb.org/~esr/sng/上看到。我还写过几个特殊的通用语言的,在我的逆向计算馆主页http://www.catb.org/retro/上都有。

I had already heard just enough about Python to know that it is what is nowadays called a scripting language, an interpretive language with its own built-in memory management and good facilities for calling and cooperating with other programs. So I dived into Programming Python with one question uppermost in my mind: what has this got that Perl does not?

我已经听到相当多的关于Python的事,知道它是一种目前所谓的脚本语言,或者解释性语言,拥有内建内存管理以及良好的调用或协调其他程序的能力。所以我开始深思一个问题:Python在什么方面比Perl更强?

Perl, of course, is the 800-pound gorilla of modern scripting languages. It has largely replaced shell as the scripting language of choice for system administrators, thanks partly to its comprehensive set of UNIX library and system calls, and partly to the huge collection of Perl modules built by a very active Perl community. The language is commonly estimated to be the CGI language behind about 85% of the live content on the Net. Larry Wall, its creator, is rightly considered one of the most important leaders in the Open Source community, and often ranks third behind Linus Torvalds and Richard Stallman in the current pantheon of hacker demigods.

Perl,在如今的脚本语言界,貌似一个800磅的庞然大物。对于系统管理员来说,它作为脚本语言,拥有超多的可替换的shell供选择,这一方面要感谢它广泛的UNIX库集和系统调用,另一方面归功于活跃的Perl社区所提供的巨多的模块。据估计,目前网上85%内容的是由它做后台CGI语言的。它的作者Larry Wall也被认为是开源社区的最重要的领导者之一,是当前骨灰级hacker殿堂中,仅次于Linus Torvalds和Richard Stallman,排名第三的人物。

At that time, I had used Perl for a number of small projects. I'd found it quite powerful, even if the syntax and some other aspects of the language seemed rather ad hoc and prone to bite one if not used with care. It seemed to me that Python would have quite a hill to climb as yet another scripting language, so as I read, I looked first for what seemed to set it apart from Perl.

那时,我用Perl编过很多小的项目。即便它的格式和其他一些方面看似很散漫,而且一不小心就很容易犯错,我仍然觉得它确实很强大。对于我来说,Python,作为脚本语言,只是另一座需要翻越的小山。所以当我开始读它的时候,只是将它看作Perl的一个分集而已。

I immediately tripped over the first odd feature of Python that everyone notices: the fact that whitespace (indentation) is actually significant in the language syntax. The language has no analog of the C and Perl brace syntax; instead, changes in indentation delimit statement groups. And, like most hackers on first realizing this fact, I recoiled in reflexive disgust.

我很快就陷入了Python的第一个奇怪特性中,或许大家都注意到了:事实上,空格(缩进)在这门语言的格式中是有重要意义的。它不像C和Perl那样用大括号{}来区分,去而代之的是用缩进来划分语句块。于是,象大多数一开始意识到这一点的hacker那样,我自然而然地选择了退却。

I am just barely old enough to have programmed in batch FORTRAN for a few months back in the 1970s. Most hackers aren't these days, but somehow our culture seems to have retained a pretty accurate folk memory of how nasty those old-style fixed-field languages were. Indeed, the term free format, used back then to describe the newer style of token-oriented syntax in Pascal and C, has almost been forgotten; all languages have been designed that way for decades now. Or almost all, anyway. It's hard to blame anyone, on seeing this Python feature, for initially reacting as though they had unexpectedly stepped in a steaming pile of dinosaur dung.

我经历过70年代用FORTRAN花了几个月编程的事,大多数hacker都没有做过那样的事,但人们的记忆却将这段保留得很清楚:旧式的位置固定的语言有多么令人反感。确实,这类在Pascal和C中被用来描述更新式的面向令牌的形式的自由格式,已经差不多被人遗忘。数十年来,所有的,或者说是几乎所有的语言都是用那种方法设计的。当看到Python的这一特性时,很难指责那些人表现出好像不小心踩到一堆湿热的恐龙粪时的反应。【这段翻得不好,谁改改~】

That's certainly how I felt. I skimmed through the rest of the language description without much interest. I didn't see much else to recommend Python, except maybe that the syntax seemed rather cleaner than Perl's and the facilities for doing basic GUI elements like buttons and menus looked fairly good.

那确实就是我的感受,我毫无兴趣地飞快地扫了一下Python语言介绍的剩下部分。除了看似格式比Perl更清晰一点,以及基本的象按钮和菜单一类的GUI元素看上去还不错以外,我看不出来Python有什么好推荐的。

I put the book back on the shelf, making a mental note that I should code some kind of small GUI-centered project in Python sometime, just to make sure I really understood the language. But I didn't believe what I'd seen would ever compete effectively with Perl.

我把书放回到书架,心里记着或许我可以用Python写些小的GUI为主的项目,来证明我已经真正地理解了这门语言。但我并不认为我所看到的这些足以有效地与Perl抗衡。

A lot of other things conspired to keep that note way down on my priority list for many months. The rest of 1997 was eventful for me; it was, among other things, the year I wrote and published the original version of The Cathedral and the Bazaar. But I did find time to write several Perl programs, including two of significant size and complexity. One of them, keeper, is the assistant still used to file incoming submissions at the Metalab software archive. It generates the web pages you see at http://metalab.unc.edu/pub/Linux/!INDEX.html. The other, anthologize, was used to automatically generate the PostScript for the sixth edition of Linux from the Linux Documentation Project's archive of HOWTOs. Both programs are available at Metalab.

后来的许多个月,心中的这个小愿望总是在不经意间被许多其他的事情挤掉了位子。1997年剩余的日子对我而言很有意义:其中就包括我写的并出版的《The Cathedral and the Bazaar》的第一版。但我确实有抽出些空来写几个Perl的程序,包括两个很具规模和复杂度的。一个是keeper,现在仍被用来在Metalab软件打包中做提交归档的。【这句谁改改?】你可以在http://metalab.unc.edu/pub/Linux/!INDEX.html里找到它。另一个是anthologize,用来自动为第六版的Linux生成PostScript的。【还有这句】这两个程序都可以在Metalab里用。

Writing these programs left me progressively less satisfied with Perl. Larger project size seemed to magnify some of Perl's annoyances into serious, continuing problems. The syntax that had seemed merely eccentric at a hundred lines began to seem like a nigh-impenetrable hedge of thorns at a thousand. More than one way to do it lent flavor and expressiveness at a small scale, but made it significantly harder to maintain consistent style across a wider code base. And many of the features that were later patched into Perl to address the complexity-control needs of bigger programs (objects, lexical scoping, use strict, etc.) had a fragile, jerry-rigged feel about them.

写这些程序让我对Perl产生了日益增多的不满,更大的项目好像让Perl的小烦恼变成很严重,而且持续不断的大麻烦。格式的问题,在百行代码量时还只是显得有些古怪,而到了上千行时就变得芒刺加身了。小规模时,多种的实现方法带来了不同的风味和表现力,但到了更大代码量时,维护统一的风格就显得极其困难了。而且,后来的许多新的特性被补入Perl使得定位复杂度控制变成了一项更大的工程(对象,字典范围,用户限制等等),让人感觉有些脆弱,而且被人操纵。

These problems combined to make large volumes of Perl code seem unreasonably difficult to read and grasp as a whole after only a few days' absence. Also, I found I was spending more and more time wrestling with artifacts of the language rather than my application problems. And, most damning of all, the resulting code was ugly--this matters. Ugly programs are like ugly suspension bridges: they're much more liable to collapse than pretty ones, because the way humans (especially engineer-humans) perceive beauty is intimately related to our ability to process and understand complexity. A language that makes it hard to write elegant code makes it hard to write good code.

这些问题综合在一起,让Perl大段的代码看起来很难阅读或者抓住要领,即使仅仅隔了几天而已。后来,我发现我正在花越来越多的时间同语言本身做斗争而不是我的应用问题。而且,更令人头痛的是,这些代码很难看!这是个很严重的问题。难看的程序就像是难看的斜拉桥:它们比漂亮的更容易被摧毁,因为人们(当然是那些工程师们)所感受到的漂亮是与我们处理和理解复杂度的能力息息相关的。对于语言来说,如果写不出优雅的代码也就写不出好的代码。

With a baseline of two dozen languages under my belt, I could detect all the telltale signs of a language design that had been pushed to the edge of its functional envelope. By mid-1997, I was thinking there has to be a better way and began casting about for a more elegant scripting language.

凭着我所掌握的数十种语言,我可以分辨出一门语言所有的…………【help me】。到了97年中,我想一定有更好的办法,并开始寻找一门更高雅的脚本语言。

One course I did not consider was going back to C as a default language. The days when it made sense to do your own memory management in a new program are long over, outside of a few specialty areas like kernel hacking, scientific computing and 3-D graphics--places where you absolutely must get maximum speed and tight control of memory usage, because you need to push the hardware as hard as possible.

我不会再考虑把C作为默认的语言,那些要靠自己来做新程序中的内存管理的日子已经过去,一些特殊的领域比如内核hacking,科学计算和三维图像都需要获取最大速度和严格的内存控制,因为你必须让硬件尽可能的“硬”。【我想是更可靠,更健壮的意思】

For most other situations, accepting the debugging overhead of buffer overruns, pointer-aliasing problems, malloc/free memory leaks and all the other associated ills is just crazy on today's machines. Far better to trade a few cycles and a few kilobytes of memory for the overhead of a scripting language's memory manager and economize on far more valuable human time. Indeed, the advantages of this strategy are precisely what has driven the explosive growth of Perl since the mid-1990s.

还有其他的情况如,接受缓存溢出的开销调试,指针引用问题,分配/释放内存泄漏和其他一些相关的仍然在现代的机器上撒野。花一些时间和代码去处理脚本语言的内存管理开销是很值得的,那可以节省更多有价值的人工。确实,自从90年代中开始,这种有用的观念已经驱使Perl飞速的成熟。

I flirted with Tcl, only to discover quickly that it scales up even more poorly than Perl. Old LISPer that I am, I also looked at various current dialects of Lisp and Scheme--but, as is historically usual for Lisp, lots of clever design was rendered almost useless by scanty or nonexistent documentation, incomplete access to POSIX/UNIX facilities, and a small but nevertheless deeply fragmented user community. Perl's popularity is not an accident; most of its competitors are either worse than Perl for large projects or somehow nowhere near as useful as their theoretically superior designs ought to make them.

我尝试Tcl,却很快发现它的规模化比Perl更糟糕。老的LISPer,就像我,也看了各种各样的Lisp的方言和Scheme,但是就像Lisp一贯的作风,由于缺乏或者没有文档、不能完全接入POSIX/UNIX设备,以及那些虽然很好但很少的用户社区,使得许多聪明的设计变得毫无价值。Perl的流行并非空穴来风。它的大多数竞争者要么就是在大项目上表现得比Perl更差,要么就是不像它们理论上所应该表现出的那么优越。

My second look at Python was almost as accidental as my first. In October 1997, a series of questions on the fetchmail-friends mailing list made it clear that end users were having increasing trouble generating configuration files for my fetchmail utility. The file uses a simple, classically UNIX free-format syntax, but can become forbiddingly complicated when a user has POP3 and IMAP accounts at multiple sites. As an example, see Listing 1 for a somewhat simplified version of mine.

我第二次看Python就像第一次那么偶然。1997年的10月,在fetchmail邮件列表中的一系列问题,说明许多终端用户正在让我的fetchmail应用增加很多错误配置文件。这个文件本身使用简单的、经典的UNIX free-format格式,但有可能当多点用户拥有POP3和IMAP账号时变得异常的复杂。这个例子,可以看一下Listing 1,是一些我的简化版本。【好像没有…】

1.1. Listing 1

I decided to attack the problem by writing an end-user-friendly configuration editor, fetchmailconf. The design objective of fetchmailconf was clear: to completely hide the control file syntax behind a fashionable, ergonomically correct GUI interface replete with selection buttons, slider bars and fill-out forms.

我决定通过写一个叫做fetchmailconf的终端用户友好的配置编辑器来解决这个问题。设计它的目的很明确:将控制文件的格式完全隐藏在一个流行的、符合审美观的GUI后,它应该由选择按钮、滚动条和填写框组成。

The thought of implementing this in Perl did not thrill me. I had seen GUI code in Perl, and it was a spiky mixture of Perl and Tcl that looked even uglier than my own pure-Perl code. It was at this point I remembered the bit I had set more than six months earlier. This could be an opportunity to get some hands-on experience with Python.

用Perl来实现这个的念头很快打消了。我看过用Perl编的GUI的代码,就是一大串Perl和Tcl的混合而已,看起来比我用纯Perl编的还难看。正是这一点让我想起了差不多六个月前的那个小心愿,这或许是个着手实践Python的好机会。

Of course, this brought me face to face once again with Python's pons asinorum, the significance of whitespace. This time, however, I charged ahead and roughed out some code for a handful of sample GUI elements. Oddly enough, Python's use of whitespace stopped feeling unnatural after about twenty minutes. I just indented code, pretty much as I would have done in a C program anyway, and it worked.

当然,这又让我再一次直面Python的诟病【那个pons asinorum不知道是什么,可能是这个意思】,就是空白的意义。不过这次我没管这么多,随便写了些GUI样例的代码。真是有够奇怪的,在差不多二十分钟后,Python中使用空白的不爽感觉没有了。我只是用了缩进代码,就象我一直在C程序里所做的那样,这样足矣。

That was my first surprise. My second came a couple of hours into the project, when I noticed (allowing for pauses needed to look up new features in Programming Python) I was generating working code nearly as fast as I could type. When I realized this, I was quite startled. An important measure of effort in coding is the frequency with which you write something that doesn't actually match your mental representation of the problem, and have to backtrack on realizing that what you just typed won't actually tell the language to do what you're thinking. An important measure of good language design is how rapidly the percentage of missteps of this kind falls as you gain experience with the language.

这是我的第一个惊喜,第二个在几个小时后出现在项目中,我注意到(在《Programming Python》中又找到了一些需要的新特性)我打字的速度就是编写可工作代码的速度。当我意识到这点时,我很吃惊。一个重要的衡量编程成果的方法就是看是否会经常出现这样的错误:你写出来的并不能准确地表达你心中所描述的问题,不得不返回来再实现那些你刚刚输入却没能准确告诉语言以完成你所想到的【…….】。一个重要的衡量好的设计语言的标准就是当你从这门语言中获得经验的过程中你有多少次“失足”于这类错误。

When you're writing working code nearly as fast as you can type and your misstep rate is near zero, it generally means you've achieved mastery of the language. But that didn't make sense, because it was still day one and I was regularly pausing to look up new language and library features!

当你写的能工作的代码和你打字速度一样快,而且失策率为零,这通常意味着你正在精通这门语言。但那并没有意义,因为这才是第一天,我还经常停下来翻查新的语言和库的特性。

This was my first clue that, in Python, I was actually dealing with an exceptionally good design. Most languages have so much friction and awkwardness built into their design that you learn most of their feature set long before your misstep rate drops anywhere near zero. Python was the first general-purpose language I'd ever used that reversed this process.

这是我在Python中得到的第一个结论,我正在和一门异常好的设计语言打交道。大多数语言都有如此多的不和谐及笨拙的因素被添加到它们的设计中,以致于在失策率降为零之前的很长一段时间,你都必须学习它们大多数的特性集。Python是第一个我所用过的打破这个规律的通用语言。

Not that it took me very long to learn the feature set. I wrote a working, usable fetchmailconf, with GUI, in six working days, of which perhaps the equivalent of two days were spent learning Python itself. This reflects another useful property of the language: it is compact--you can hold its entire feature set (and at least a concept index of its libraries) in your head. C is a famously compact language. Perl is notoriously not; one of the things the notion There's more than one way to do it! costs Perl is the possibility of compactness.

我并没有花太久去学习特性集,就写出了一个能工作的,有用的,带GUI的fetchmailconf。仅仅是用了六个工作日,而其中还包括差不多两天用来学习Python本身。这又反映出这门语言的另一个有用的属性:它很紧凑。你可以把它整个特性集(至少是库的索引概念)放在头脑中。C是一门著名的紧凑的语言,Perl却恰恰是另一个极端。拥有多种实现的方法让Perl很难有机会紧凑。

But my most dramatic moment of discovery lay ahead. My design had a problem: I could easily generate configuration files from the user's GUI actions, but editing them was a much harder problem. Or, rather, reading them into an editable form was a problem.

不过,最令我印象深刻的发现还在后面。我的设计有个问题:我不能简单地从用户GUI的动作中创建配置文件,但编辑它们又是一个令人头疼的问题。或者说,把它们读进一个可编辑框是个问题。

The parser for fetchmail's configuration file syntax is rather elaborate. It's actually written in YACC and Lex, two classic UNIX tools for generating language-parsing code in C. In order for fetchmailconf to be able to edit existing configuration files, I thought it would have to replicate that elaborate parser in Python. I was very reluctant to do this, partly because of the amount of work involved and partly because I wasn't sure how to ascertain that two parsers in two different languages accept the same. The last thing I needed was the extra labor of keeping the two parsers in synchronization as the configuration language evolved!

Fetchmail配置文件的分析器(Parser)相当精巧,是用YACC和Lex写的,这是两个经典的UNIX工具,用来在C中产生语言分析码的。为了让fetchmailconf能编辑已经存在的配置文件,我想可能必须得在Python中重写一个那样精巧的分析器。我非常不愿意这样做,一方面是因为牵涉到大量的工作,另一方面是我不能确定怎样知道用两种不同的语言写的不同的分析器可以一样工作。还有一件事就是我需要额外的劳动来保持两种分析器同步工作。

This problem stumped me for a while. Then I had an inspiration: I'd let fetchmailconf use fetchmail's own parser! I added a --configdump option to fetchmail that would parse .fetchmailrc and dump the result to standard output in the format of a Python initializer. For the file above, the result would look roughly like Listing 2 (to save space, some data not relevant to the example is omitted).

这个问题让我愣了一会儿,然后我有了一个灵感:我可以让fetchmailconf用fetchmail自己的分析器!我添加一个configdump的选项到fetchmail用来分析。Fetchmailrc和导出的结果用Python initializer的格式放到标准输出。以上的文件可以在Listing2中看到(为了节省空间,一些与本例不相关的数据省略)。【还是没有】

1.2. Listing 2

Python could then evaluate the fetchmail --configdump output and have the configuration available as the value of the variable fetchmail.

这样Python就可以估计出fetchmail的configdump的输出以及让配置以fetchmail的变量值的形式出现。【这句翻译得很差】

This wasn't quite the last step in the dance. What I really wanted wasn't just for fetchmailconf to have the existing configuration, but to turn it into a linked tree of live objects. There would be three kinds of objects in this tree: Configuration (the top-level object representing the entire configuration), Site (representing one of the sites to be polled) and User (representing user data attached to a site). The example file describes five site objects, each with one user object attached to it.

这还不是最完美的,我真正想要的并不只是让fetchmailconf拥有已经存在的配置,而是想把它变成一个可链接的对象树。树中有三种对象,分别是Configuration(代表了整个配置的顶层对象),Site(代表了将被登记的站点中的一个),还有User(代表了加入一个站点的用户数据)。例子中的文件有五个Site对象,每一个都加入了一个User对象。

I had already designed and written the three object classes (that's what took four days, most of it spent getting the layout of the widgets just right). Each had a method that caused it to pop up a GUI edit panel to modify its instance data. My last remaining problem was somehow to transform the dead data in this Python initializer into live objects.

我已经设计并写了这三个对象的类(差不多用了四天,主要是把时间花在了调整部件位置上)。每个类都有一个方法可以弹出一个GUI编辑层来修改实例数据。最后剩下的问题就是如何把在Python initializer中的旧数据导到新的对象中。

I considered writing code that would explicitly know about the structure of all three classes and use that knowledge to grovel through the initializer creating matching objects, but rejected that idea because new class members were likely to be added over time as the configuration language grew new features. If I wrote the object-creation code in the obvious way, it would be fragile and tend to fall out of sync when either the class definitions or the initializer structure changed.

我考虑着写些代码,可以很明确地知道三种类的结构,并用这些信息来恳求initializer创建相应的对象。如果配置语言又发展了新的特性,而新的要加进去的类成员又过时的话,就要拒绝这些请求。假如我用很明显的方法写些创建对象的代码,那么当类的定义或者initializer结构发生变化时,它会显得很脆弱,而且不太容易同步。

What I really wanted was code that would analyze the shape and members of the initializer, query the class definitions themselves about their members, and then adjust itself to impedance-match the two sets.

我所真正想要的就是代码能够分析initializer的类型和成员,能查询类所定义的成员,并且自动把它们调整到相匹配的两个集合里去。

This kind of thing is called metaclass hacking and is generally considered fearsomely esoteric--deep black magic. Most object-oriented languages don't support it at all; in those that do (Perl being one), it tends to be a complicated and fragile undertaking. I had been impressed by Python's low coefficient of friction so far, but here was a real test. How hard would I have to wrestle with the language to get it to do this? I knew from previous experience that the bout was likely to be painful, even assuming I won, but I dived into the book and read up on Python's metaclass facilities. The resulting function is shown in Listing 3, and the code that calls it is in Listing 4.

这种事就是所谓的元类hack,也经常被认为是令人望而却步而且神秘的黑色魔法。大多数面向对象的语言根本就不支持它这样。在那些能做的语言(Perl是其中一个)中,它也是一个很复杂而且很脆弱的任务。现在我已经被Python如此低的冲突系数所打动,但真正的测试才刚刚开始。我用Python来实现它会有多难呢?从以往的经历来看,这一次的较量估计会比较痛苦,即使我赢了,或许也得把Python关于元类的内容翻个底朝天。后来的函数就列在Listing3里,而调用它的代码在Listing4里。【Listing3和4都没有…哪里去找找…】

1.3. Listing 3

1.4. Listing 4 That doesn't look too bad for deep black magic, does it? Thirty-two lines, counting comments. Just from knowing what I've said about the class structure, the calling code is even readable. But the size of this code isn't the real shocker. Brace yourself: this code only took me about ninety minutes to write--and it worked correctly the first time I ran it.

对于这样一个黑色魔法来说,看起来也并不糟,不是吗?32行代码,还包括注释行,就出现了我所想要的那个类的结构,调用的代码也很容易读。但代码量还不是真正令人称道的,想想:我只花了九十分钟来写这些代码,而且我第一次运行就成功实现啦。

To say I was astonished would have been positively wallowing in understatement. It's remarkable enough when implementations of simple techniques work exactly as expected the first time; but my first metaclass hack in a new language, six days from a cold standing start? Even if we stipulate that I am a fairly talented hacker, this is an amazing testament to Python's clarity and elegance of design.

不得不承认我已经在不知不觉中迷上了它,用一些简单的技术所做出来的东西和我一开始所期待的差不多。但是这第一个元类hack仅仅用了我六天时间,而且是从零开始的一门新语言。就算说我是一个很有才华的hacker,Python的清新和高雅也足以令人称奇。

There was simply no way I could have pulled off a coup like this in Perl, even with my vastly greater experience level in that language. It was at this point I realized I was probably leaving Perl behind.

这真是太简单了,即使我用Perl已经到达了很高的境界,仍难以想象Perl也能走出这样的妙着。这时我才真正意识到我应该把Perl抛诸脑后。

This was my most dramatic Python moment. But, when all is said and done, it was just a clever hack. The long-term usefulness of a language comes not in its ability to support clever hacks, but from how well and how unobtrusively it supports the day-to-day work of programming. The day-to-day work of programming consists not of writing new programs, but mostly reading and modifying existing ones.

这就是我最钟爱的Python时刻。不过,所说的和所做的,也只是一次聪明的hack经历。一门总是有用的语言所带来的不应该仅仅是支持聪明的hack,还必须能支持日复一日的编程工作,而且得是优秀的,严谨的。这每天的编程工作指的并不是写新的程序,而更多的是读或者改已有的。

So the real punchline of the story is this: weeks and months after writing fetchmailconf, I could still read the fetchmailconf code and grok what it was doing without serious mental effort. And the true reason I no longer write Perl for anything but tiny projects is that was never true when I was writing large masses of Perl code. I fear the prospect of ever having to modify keeper or anthologize again--but fetchmailconf gives me no qualms at all.

所以,这个故事真正的重点就是:在编写fetchmailconf几周或几个月之后,我仍能不费吹灰之力地读懂fetchmailconf和grok的代码。我不再用Perl写任何东西(除了很小的项目)的真正原因就是用Perl写的大段代码从来没有让我真正满意过。我很不愿意再去修改或者分类,至少fetchmailconf从来没有给我头昏眼花的感觉。

Perl still has its uses. For tiny projects (100 lines or fewer) that involve a lot of text pattern matching, I am still more likely to tinker up a Perl-regexp-based solution than to reach for Python. For good recent examples of such things, see the timeseries and growthplot scripts in the fetchmail distribution. Actually, these are much like the things Perl did in its original role as a sort of combination awk/sed/grep/sh, before it had functions and direct access to the operating system API. For anything larger or more complex, I have come to prefer the subtle virtues of Python--and I think you will, too.

Perl仍然有它的用处,对于那些很小的(100行甚至更少的)需要大量文本模式匹配的项目来说,我还是喜欢考虑用基于Perl的解决方案,而不是直接用Python。一些新的好例子可以看看fetchmail的timeseries和growthplot脚本。实际上,这些很像Perl在拥有函数和直接接入操作系统的API之前,也就是一开始的作用那样,作为一系列awk/sed/grep/sh的混合体。而更大、更复杂的东西,我一定会优先考虑精致而高效的Python。我想你也千万别错过它。

2. 中文版

【以下翻译由Chaox完成,【括号】内的文字为译者注,此同。水平有限,还需要大家帮忙完成一些比较难的句子。 】

Cardinal Biggles让Eric舒舒服服地度过了四个多小时后,“逼着”他写下了这份感悟。【若要取之,必先予之】

我第一次看到Python是一次很偶然的机会,而且那时我并不怎么喜欢它。大概在1997年初,Mark Lutz的书《Programming Python》刚从O’Reilly & Associates出炉。O’Reilly的书偶尔会被放在我家的门阶上,它们是由组织里一些神秘的捐赠者从一堆新书中选出来的,我都懒得去考虑他们是怎么选的。

其中有一本就是《Programming Python》,我对它有那么点兴趣,因为我总是收集计算机语言。我知道超过二十种通用的语言,出于兴趣写些编译器和解释器,还设计了不少专用语言和标记性的。我最近完成的项目是一种用来处理PNG图像的称为SNG的专用语言。感兴趣的读者可以从SNG的主页http://www.catb.org/~esr/sng/上看到。我还写过几个特殊的通用语言的,在我的逆向计算馆主页http://www.catb.org/retro/上都有。

我已经听到相当多的关于Python的事,知道它是一种目前所谓的脚本语言,或者解释性语言,拥有内建内存管理以及良好的调用或协调其他程序的能力。所以我开始深思一个问题:Python在什么方面比Perl更强?

Perl,在如今的脚本语言界,貌似一个800磅的庞然大物。对于系统管理员来说,它作为脚本语言,拥有超多的可替换的shell供选择,这一方面要感谢它广泛的UNIX库集和系统调用,另一方面归功于活跃的Perl社区所提供的巨多的模块。据估计,目前网上85%内容的是由它做后台CGI语言的。它的作者Larry Wall也被认为是开源社区的最重要的领导者之一,是当前骨灰级hacker殿堂中,仅次于Linus Torvalds和Richard Stallman,排名第三的人物。

那时,我用Perl编过很多小的项目。即便它的格式和其他一些方面看似很散漫,而且一不小心就很容易犯错,我仍然觉得它确实很强大。对于我来说,Python,作为脚本语言,只是另一座需要翻越的小山。所以当我开始读它的时候,只是将它看作Perl的一个分集而已。

我很快就陷入了Python的第一个奇怪特性中,或许大家都注意到了:事实上,空格(缩进)在这门语言的格式中是有重要意义的。它不像C和Perl那样用大括号{}来区分,去而代之的是用缩进来划分语句块。于是,象大多数一开始意识到这一点的hacker那样,我自然而然地选择了退却。

我经历过70年代用FORTRAN花了几个月编程的事,大多数hacker都没有做过那样的事,但人们的记忆却将这段保留得很清楚:旧式的位置固定的语言有多么令人反感。确实,这类在Pascal和C中被用来描述更新式的面向令牌的形式的自由格式,已经差不多被人遗忘。数十年来,所有的,或者说是几乎所有的语言都是用那种方法设计的。当看到Python的这一特性时,很难指责那些人表现出好像不小心踩到一堆湿热的恐龙粪时的反应。【这段翻得不好,谁改改~】

那确实就是我的感受,我毫无兴趣地飞快地扫了一下Python语言介绍的剩下部分。除了看似格式比Perl更清晰一点,以及基本的象按钮和菜单一类的GUI元素看上去还不错以外,我看不出来Python有什么好推荐的。

我把书放回到书架,心里记着或许我可以用Python写些小的GUI为主的项目,来证明我已经真正地理解了这门语言。但我并不认为我所看到的这些足以有效地与Perl抗衡。

后来的许多个月,心中的这个小愿望总是在不经意间被许多其他的事情挤掉了位子。1997年剩余的日子对我而言很有意义:其中就包括我写的并出版的《The Cathedral and the Bazaar》的第一版。但我确实有抽出些空来写几个Perl的程序,包括两个很具规模和复杂度的。一个是keeper,现在仍被用来在Metalab软件打包中做提交归档的。【这句谁改改?】你可以在http://metalab.unc.edu/pub/Linux/!INDEX.html里找到它。另一个是anthologize,用来自动为第六版的Linux生成PostScript的。【还有这句】这两个程序都可以在Metalab里用。

写这些程序让我对Perl产生了日益增多的不满,更大的项目好像让Perl的小烦恼变成很严重,而且持续不断的大麻烦。格式的问题,在百行代码量时还只是显得有些古怪,而到了上千行时就变得芒刺加身了。小规模时,多种的实现方法带来了不同的风味和表现力,但到了更大代码量时,维护统一的风格就显得极其困难了。而且,后来的许多新的特性被补入Perl使得定位复杂度控制变成了一项更大的工程(对象,字典范围,用户限制等等),让人感觉有些脆弱,而且被人操纵。

这些问题综合在一起,让Perl大段的代码看起来很难阅读或者抓住要领,即使仅仅隔了几天而已。后来,我发现我正在花越来越多的时间同语言本身做斗争而不是我的应用问题。而且,更令人头痛的是,这些代码很难看!这是个很严重的问题。难看的程序就像是难看的斜拉桥:它们比漂亮的更容易被摧毁,因为人们(当然是那些工程师们)所感受到的漂亮是与我们处理和理解复杂度的能力息息相关的。对于语言来说,如果写不出优雅的代码也就写不出好的代码。

凭着我所掌握的数十种语言,我可以分辨出一门语言所有的…………【help me】。到了97年中,我想一定有更好的办法,并开始寻找一门更高雅的脚本语言。

我不会再考虑把C作为默认的语言,那些要靠自己来做新程序中的内存管理的日子已经过去,一些特殊的领域比如内核hacking,科学计算和三维图像都需要获取最大速度和严格的内存控制,因为你必须让硬件尽可能的“硬”。【我想是更可靠,更健壮的意思】

还有其他的情况如,接受缓存溢出的开销调试,指针引用问题,分配/释放内存泄漏和其他一些相关的仍然在现代的机器上撒野。花一些时间和代码去处理脚本语言的内存管理开销是很值得的,那可以节省更多有价值的人工。确实,自从90年代中开始,这种有用的观念已经驱使Perl飞速的成熟。

我尝试Tcl,却很快发现它的规模化比Perl更糟糕。老的LISPer,就像我,也看了各种各样的Lisp的方言和Scheme,但是就像Lisp一贯的作风,由于缺乏或者没有文档、不能完全接入POSIX/UNIX设备,以及那些虽然很好但很少的用户社区,使得许多聪明的设计变得毫无价值。Perl的流行并非空穴来风。它的大多数竞争者要么就是在大项目上表现得比Perl更差,要么就是不像它们理论上所应该表现出的那么优越。

我第二次看Python就像第一次那么偶然。1997年的10月,在fetchmail邮件列表中的一系列问题,说明许多终端用户正在让我的fetchmail应用增加很多错误配置文件。这个文件本身使用简单的、经典的UNIX free-format格式,但有可能当多点用户拥有POP3和IMAP账号时变得异常的复杂。这个例子,可以看一下Listing 1,是一些我的简化版本。【好像没有…】

1.1. Listing 1

我决定通过写一个叫做fetchmailconf的终端用户友好的配置编辑器来解决这个问题。设计它的目的很明确:将控制文件的格式完全隐藏在一个流行的、符合审美观的GUI后,它应该由选择按钮、滚动条和填写框组成。

用Perl来实现这个的念头很快打消了。我看过用Perl编的GUI的代码,就是一大串Perl和Tcl的混合而已,看起来比我用纯Perl编的还难看。正是这一点让我想起了差不多六个月前的那个小心愿,这或许是个着手实践Python的好机会。

当然,这又让我再一次直面Python的诟病【那个pons asinorum不知道是什么,可能是这个意思】,就是空白的意义。不过这次我没管这么多,随便写了些GUI样例的代码。真是有够奇怪的,在差不多二十分钟后,Python中使用空白的不爽感觉没有了。我只是用了缩进代码,就象我一直在C程序里所做的那样,这样足矣。

这是我的第一个惊喜,第二个在几个小时后出现在项目中,我注意到(在《Programming Python》中又找到了一些需要的新特性)我打字的速度就是编写可工作代码的速度。当我意识到这点时,我很吃惊。一个重要的衡量编程成果的方法就是看是否会经常出现这样的错误:你写出来的并不能准确地表达你心中所描述的问题,不得不返回来再实现那些你刚刚输入却没能准确告诉语言以完成你所想到的【…….】。一个重要的衡量好的设计语言的标准就是当你从这门语言中获得经验的过程中你有多少次“失足”于这类错误。

当你写的能工作的代码和你打字速度一样快,而且失策率为零,这通常意味着你正在精通这门语言。但那并没有意义,因为这才是第一天,我还经常停下来翻查新的语言和库的特性。

这是我在Python中得到的第一个结论,我正在和一门异常好的设计语言打交道。大多数语言都有如此多的不和谐及笨拙的因素被添加到它们的设计中,以致于在失策率降为零之前的很长一段时间,你都必须学习它们大多数的特性集。Python是第一个我所用过的打破这个规律的通用语言。

我并没有花太久去学习特性集,就写出了一个能工作的,有用的,带GUI的fetchmailconf。仅仅是用了六个工作日,而其中还包括差不多两天用来学习Python本身。这又反映出这门语言的另一个有用的属性:它很紧凑。你可以把它整个特性集(至少是库的索引概念)放在头脑中。C是一门著名的紧凑的语言,Perl却恰恰是另一个极端。拥有多种实现的方法让Perl很难有机会紧凑。

不过,最令我印象深刻的发现还在后面。我的设计有个问题:我不能简单地从用户GUI的动作中创建配置文件,但编辑它们又是一个令人头疼的问题。或者说,把它们读进一个可编辑框是个问题。

Fetchmail配置文件的分析器(Parser)相当精巧,是用YACC和Lex写的,这是两个经典的UNIX工具,用来在C中产生语言分析码的。为了让fetchmailconf能编辑已经存在的配置文件,我想可能必须得在Python中重写一个那样精巧的分析器。我非常不愿意这样做,一方面是因为牵涉到大量的工作,另一方面是我不能确定怎样知道用两种不同的语言写的不同的分析器可以一样工作。还有一件事就是我需要额外的劳动来保持两种分析器同步工作。

这个问题让我愣了一会儿,然后我有了一个灵感:我可以让fetchmailconf用fetchmail自己的分析器!我添加一个configdump的选项到fetchmail用来分析。Fetchmailrc和导出的结果用Python initializer的格式放到标准输出。以上的文件可以在Listing2中看到(为了节省空间,一些与本例不相关的数据省略)。【还是没有】

1.2. Listing 2

这样Python就可以估计出fetchmail的configdump的输出以及让配置以fetchmail的变量值的形式出现。【这句翻译得很差】

这还不是最完美的,我真正想要的并不只是让fetchmailconf拥有已经存在的配置,而是想把它变成一个可链接的对象树。树中有三种对象,分别是Configuration(代表了整个配置的顶层对象),Site(代表了将被登记的站点中的一个),还有User(代表了加入一个站点的用户数据)。例子中的文件有五个Site对象,每一个都加入了一个User对象。

我已经设计并写了这三个对象的类(差不多用了四天,主要是把时间花在了调整部件位置上)。每个类都有一个方法可以弹出一个GUI编辑层来修改实例数据。最后剩下的问题就是如何把在Python initializer中的旧数据导到新的对象中。

我考虑着写些代码,可以很明确地知道三种类的结构,并用这些信息来恳求initializer创建相应的对象。如果配置语言又发展了新的特性,而新的要加进去的类成员又过时的话,就要拒绝这些请求。假如我用很明显的方法写些创建对象的代码,那么当类的定义或者initializer结构发生变化时,它会显得很脆弱,而且不太容易同步。

我所真正想要的就是代码能够分析initializer的类型和成员,能查询类所定义的成员,并且自动把它们调整到相匹配的两个集合里去。

这种事就是所谓的元类hack,也经常被认为是令人望而却步而且神秘的黑色魔法。大多数面向对象的语言根本就不支持它这样。在那些能做的语言(Perl是其中一个)中,它也是一个很复杂而且很脆弱的任务。现在我已经被Python如此低的冲突系数所打动,但真正的测试才刚刚开始。我用Python来实现它会有多难呢?从以往的经历来看,这一次的较量估计会比较痛苦,即使我赢了,或许也得把Python关于元类的内容翻个底朝天。后来的函数就列在Listing3里,而调用它的代码在Listing4里。【Listing3和4都没有…哪里去找找…】

1.3. Listing 3

1.4. Listing 4

对于这样一个黑色魔法来说,看起来也并不糟,不是吗?32行代码,还包括注释行,就出现了我所想要的那个类的结构,调用的代码也很容易读。但代码量还不是真正令人称道的,想想:我只花了九十分钟来写这些代码,而且我第一次运行就成功实现啦。

不得不承认我已经在不知不觉中迷上了它,用一些简单的技术所做出来的东西和我一开始所期待的差不多。但是这第一个元类hack仅仅用了我六天时间,而且是从零开始的一门新语言。就算说我是一个很有才华的hacker,Python的清新和高雅也足以令人称奇。

这真是太简单了,即使我用Perl已经到达了很高的境界,仍难以想象Perl也能走出这样的妙着。这时我才真正意识到我应该把Perl抛诸脑后。

这就是我最钟爱的Python时刻。不过,所说的和所做的,也只是一次聪明的hack经历。一门总是有用的语言所带来的不应该仅仅是支持聪明的hack,还必须能支持日复一日的编程工作,而且得是优秀的,严谨的。这每天的编程工作指的并不是写新的程序,而更多的是读或者改已有的。

所以,这个故事真正的重点就是:在编写fetchmailconf几周或几个月之后,我仍能不费吹灰之力地读懂fetchmailconf和grok的代码。我不再用Perl写任何东西(除了很小的项目)的真正原因就是用Perl写的大段代码从来没有让我真正满意过。我很不愿意再去修改或者分类,至少fetchmailconf从来没有给我头昏眼花的感觉。

Perl仍然有它的用处,对于那些很小的(100行甚至更少的)需要大量文本模式匹配的项目来说,我还是喜欢考虑用基于Perl的解决方案,而不是直接用Python。一些新的好例子可以看看fetchmail的timeseries和growthplot脚本。实际上,这些很像Perl在拥有函数和直接接入操作系统的API之前,也就是一开始的作用那样,作为一系列awk/sed/grep/sh的混合体。而更大、更复杂的东西,我一定会优先考虑精致而高效的Python。我想你也千万别错过它。

3. 反馈

WhyPython/WhyPythonZh (last edited 2009-12-25 07:14:44 by localhost)