重写Reddit
英文原文: Rewriting Reddit
2012 年注:本文首发于 2005 年。发布之后,Django 上线了一个 RemovingTheMagic 项目,提出了我的一些质疑(尽管我本人发现它仍然不可用), web.py 促进了 FriendFeed 的 tornado.web 和 Google 的 gae.webapp 以及其它项目(尽管如此我仍然喜欢 web.py),本文引起了 Reddit 流量的永久井喷,仍然没有真正地停止增长。
在 reddit.com 过去的一周里,我们用 Python 代替 Lisp 重写了网站。这周我们做了很多工作(透露:我们使用了我开发的 web.py 类库)。其他人熟悉 Lisp(整个站点用它编写),他们喜欢 Python(他们用它重写了整个站点),然而他们决定,在这个项目中喜欢 Python 多一些。Python 版本的网站需要更少的代码,运行更快、代码更容易阅读和维护。
根据在 reddit 博客上的评论,比 Lisp 更好的东东的想法,表面上对一些人是不可思议的。Lisp 爱好者们很快着手尽量找到变换语言背后的真正原因。
有人认为这里面一定有不同寻常的干预,由于“貌似没有切换到低等语言的其它原因。”又有人觉得其它事情必须在发生着:“这会是……一个谎言?扔掉竞争力?貌似 Paul Graham 没有在他的文章里暗示这个策略呀……”还有人附和道:“我认为这是恶作剧。”也有人提出,作者只是想更多地“走捷径、hack 和伪装手法。”
当然,它们都是极端的情况。其它人猜测一定有来自外部的压力。“我猜,要么是类库,要么是雇佣新员工。”有人总结到:“一些投资商想要任何程序员都能维护的产品。我希望他给你支付了好多钱。”
Lisp 新闻组 comp.lang.lisp 对于这次切换感到失望,他们为了表明自己是多么地正确,最近正 计划把 reddit 写成 Lisp 的一个竞争者。
这些说法当中,稍微中肯的提到 Lisp 的价值在于能够创建 新的语言结构,这对于简单的 web 应用程序而言,是不必要的,因为结构已经被建好了。不过,这也不是真实的。web.py 完全从零起步,用到了各种“新的语言结构”(linguistic constructs)——甚至更好——这些结构具有语法,让它们具有合理的可读性。当然,Python 不是 Perl 6,因此你不能增加任意语法,不过你经常能够找到把事情搞定的聪明方法。
另一方面,Python 有它自己的问题。最大的问题在于它有很多 web 应用程序框架,但是都不太好。Python 爱好者只是注意到了第一句,而明显忽视了第二句,因为当我告诉他们我正在用自己的类库时,通常的反应是“我认为 Python 不需要另一个 web 应用程序框架”。是的,Python 需要更少的 web 应用程序框架,但是它也需要不是太糟糕的框架。
貌似最有前途的框架就是 Django 了,我们最初的确想用它重写 reddit。做为有经验的 Python 程序员,我尽量帮助其他人解脱出来。
从外面看 Django 貌似优秀:良好外观的网站、极具才能和天赋的开发者,表面过剩的、优秀功能。开发者和社区是非常有帮助的,也对补丁和建议作出响应。所有正确的目标都在他们的哲学文档和 FAQ 得以支持。然而不幸的是,它们好像完全没有达到自己的期望。
Django 声称它是“松耦合”的,却要求你的代码符合 Django 的风格。Django 坚持你的代码执行本身,或是通过命令行工具,或是借助正确的环境变量和 Python 路径做特定的服务器处理程序调用。当你开始项目时,Django 默认为代码生成了嵌套四层的文件夹,而你能够移动一些文件,我在搞清楚移动哪一个以及如何移动上遇到了麻烦。
Django 的哲学是“明确优于隐式”,但是它却有各种魔法。你在一个文件创建的数据库模型,神奇地出现在有着不同名字的 Django 模块内部的某个地方。当模型功能被调用时,新东东被添到了它的变量里,旧的数据被移除了。(我被告知,他们目前正在修复这两个问题。)
Django 的另一个目标是“较少的代码”,至少对你而言。但是 Django 却充满了代码。在 Django 里有 10 个不同的文件夹、而每个文件夹内部又有一些文件夹。当你根据 Django 教程开始实际地建立网站时,你已经导入了 django.core.meta, django.models.polls,django.conf.urls.defaults.*,django.utils.httpwrappers.HttpResponse 和 django.core.extensions.render_to_response。任何人应当记住它们,不是明确的,尤其是好像没有原理或如何命名的指导原则。上面的三个模块被开始的脚本自动插入了,但是你仍然需要为你想使用的每个函数记住这些名字。
不过,Django 最重要的问题在于,它的开发者貌似未能设计出得体的 API。他们肯定是有能力的 Python 程序员——他们的代码用到了各种奇怪的手法。他们肯定能写出可运行的代码——它们有各种有趣的功能。但是,他们好像不能把代码构造成其他人可用的方式。
他们的 API 是丑陋的,定期地丢失重要功能:数据库 API 通过计算下划线来构造查询,但是没有专门的语法来处理 JOIN,模板系统需要 4 个大括号包围每个变量,却不能做任意种类的计算,表单 API 需要 15 行来处理表单,却不能自动生成模板。
我尽力修复这些问题了——Django 社区相当支持——但是任务让我退缩了。我只是精神上做不到,更不要说不得不为我自己的创业去实际开发自己的应用程序的、时间限制。
因此,Lisp 和 Django 是不足的,我们带着 web.py 离开了。我想说,web.py 认识到了这些错误,在设计的时候就避免了它们,不过真相是,web.py 在出现这些问题之前已经被编写了,并设法避免了它们。
我写 web.py 的方式是简单的:我想象着事情应该如何运行,然后我让它们成为现实。有时候让它运行需要很多代码。有时候它只需要一点儿代码。但是不管怎样,这些事实对于用户是不可见的——它们只需要完美的 API。
那么应该如何运行呢?第一个原则是,代码应该清晰简单。如果你想输出文本,你就调用 web.output。如果你想得到表单输入,你就调用 web.input。没有特别难记的东东。
第二个原则,web.py 应该适合你的代码,而不是其它方式。web.py 里的每个函数都是完全独立的,你想用哪个就用哪个。你可以把文件随意放在任何地方,web.py 乐于定位到它。如果你想把一块代码做为 web 应用程序运行,你可以调用 web.run,你不要把代码放在不可思议的地方,让 web.py 运行。
第三个原则是,web.py 默认应该做 web 所认为的正确的事情。这意味着正确地区分 GET 和 POST。它代表了简单、同一性会跳转到标准链接。它代表了含有正确 HTTP 头的、可读的 HTML。
我所担心的是,有太多你需要的原则。它们对我来说,好像非常简单和明显,甚至我乐意篡改它们,但是其它 Python 的 web 应用程序框架却没有和它比肩的。(如果你知道有,请告诉我,我乐于收回上面的话。我想不是这样的。)在此之前,貌似我被迫做了一件本不愿意做的、可怕的事情:向世界又提交了一个 Python web 应用程序框架。