可爱的 Python:Python实现内幕

采访 Vyper 和 Stackless Python 创始人

Comments

据我了解,现在可以下载并运行四种 Python 的实现,还有一种实现正在创建中。每种实现都有其存在的特殊理由,这些理由可以在这里从实现开发者自己的话语里了解到。

对不同的平台重新编译编译器或解释器所产生的实现只是略有不同(可能有少量的条件性编译和更改),但最有趣的实现(就我而言)是超越平台问题的那些。实际上,我们在这篇文章中将要看到的那些 Python 实现大多本身就是多平台。实现的概念也与 版本的概念有所区别。就语言特性而言,这里谈到的所有实现基本上都处于同一语言版本 (1.5.2)。很明显,CPython 1.6/2.0/3000 已经有一个部分属于新的基本实现,但其它实现可以同样地与那些语言级别的特性一致。

重新实现了哪些编程语言,实现的频率怎样,出于什么原因,以及由谁实现?要形容这组语言非常困难。某些与 Python 几乎处同一地位的流行语言 -- 例如 perl、REBOL 和 PHP -- 只有一种实现(编译成许多平台)。TCL 与 Perl/PHP 最为相似,但 确实有一种称为 Jacl 的 Java 平台版本。从另一个极端来看,例如 C、Awk、Cobol、REXX 和 Java 这样的语言,每个都曾经被无数次地实现。但那些再实现是为了许可和营销等考虑,而不是出于实现的概念和抽象问题。似乎有特殊学术意味的那些语言重新实现得很多(特别是函数性、逻辑性或超纯 OOP 语言,例如Smalltalk 和 Eiffel)。Lisp 没有几百个也有几十个实现和派生。

与我们将要讨论的 Python 实现不同,Lisp 的派生在提供新实现的同时往往引入许多新颖的 语言特性。Python 实现在很大程度上实现和主要 CPython 版本 相同的 Python 语言。所有现有的版本都是开放源码合作努力的结果,这种情况下,创新与市场定位没有太大关系,甚至与有时导致开放源码项目分裂的许可证争斗也没有什么关系。而且,不同的 Python 版本也不是真正传统意义上的 支流,而集中于不同的概念,正是这些概念证明它本身就是 Python 实现。

两种没有详细说明的实现是 JPython 和 Python.NET。JPython 是以 Java 编写的编译器,用于将 Python 源代码编译成 Java 字节码。Python 应用程序最终是在 JVM(用户可能不知道它是以 Python 源代码而不是 Java 编写的,他们也不需要关心)中运行的。Python.NET 是个还未交付的实现,但它 -- 至少在结构上 -- 将与 JPython 相似。Python.NET 将让 Python 参与到微软的 .NET 项目中,该项目基本上接近于一个可以运行以各种语言(例如新的 C#、Visual、Basic、C++,以及 Python)编写的程序的非 Java VM。请随时关注这些实现的开发者发布的信息。

有两种从理论角度上讲使人着迷的实现,下面,就让我们听听它们的开发者都说了些什么:开发 Vyper的 John Max Skaller 和开发 Stackless Python的 Christian Tismer。

Vyper:采访创始人 John Max Skaller

Vyper 是以函数性语言 Ocaml (3.00) 编写的 Python 语言的实现。与其它 Python 实现比较,Vyper 提供了一些(可选的)语言扩展:更强大的范围确定规则和一些新的函数性特性。Vyper 现在不再进行开发,但它以后可能得到增强(请参阅 参考资料获得 Vyper,以及它的源代码。)。我问 Vyper 的创始人 John Max Skaller 有关建造 Vyper 的动机。

Skaller:建造 Vyper 有两个原因:首先,我喜欢 Python,特别是它的简单性。但我不喜欢它缺乏范围确定性,凡事都需要做大量更改来取得进展。因此我决定在保留与 Python 兼容性的同时,通过建造高级得多的编程语言,并在其中建造函数性编程语言的某些概念来改正这些问题。
第二个原因是性能。我有一个称之为 interscript 的主要 Python 程序,一个有读写能力的编程 (LP) 工具,它不仅受到 Python 中缺乏良好结构(如上所述),而且还受性能问题的困扰。
Mertz:既然文字编程是创建 Vyper 的一个动机,您是否能简单介绍一下什么是有读写能力的编程,我想这样对读者会有所帮助。
Skaller:它的构思是不需要为程序记录文档(在编程后),而是编写 包含程序的文档。[它]由 Donald Knuth 发明。
interscript 独立于排字和编程语言,可以通过以 Python 编写的任意可执行代码 在文档中扩展。即,尽管有大量预构建的构造可以满足“日常”需要,但一个人可以随意 生成代码和文档。
但除非 LP 很快,否则它永远不能作为主流技术接纳。我花费了许多工作使它变得快一些,但结果,Python 还是无法快到执行所需要的操作:解释语言中逐个字符地处理字符串就是无法快起来。
所以想要构建一个 Python 编译器,至少它能生成可以优化这种代码的机器二进制文件。这是某些 Vyper 扩展的一个原因,从而使优化成为可能。
我从来没有编写过编译器;构思是编写一个解释器,可以在编译时装入所有程序模块,然后将产生的字典 冻结 到可执行二进制文件中。现在的 Vyper 就是这样的解释器,我在扩展语言的过程中找到了很多乐趣,但我找到了一份编写编译器的带薪工作,现在就没有时间继续这一工作了。
Mertz:Vyper 的一个特别新颖的特性是其以 Ocaml 的实现。许多读者可能认为编译器/解释器是以 C 实现的(与本质接近);或者对于已定义的机器,编译器可以以 Python 自身实现。为什么使用 Ocaml?
Skaller:Ocaml 直接生成机器代码。相对于 C 来说,它运行得相当不错,对于某些工作甚至会更快。它还带有一个无用信息收集器。Ocaml 是一种高级语言,与 C、C++、Python 或大多数其它所谓的“高级”语言不一样。
Ocaml 和 Python 一样,是混合的函数性/命令语言。Vyper 比 Python 更多地强调 Python 的函数性方面。它纠正了一些明显的设计缺陷,特别是缺少词法范围确定的问题。
在实际中,函数性编程后面有强大的理论支持,而对于命令编程则没有任何理论支持。这意味着从开发角度来说,函数性编程语言比任何命令编程语言通常要好得多,但往往缺乏与基本硬件规则体系结构相近的系统性能。

有意思的是,下一种实现,尽管来自不同角度,在某些方面要胜过 Vyper:

Skaller:该项目的另一致命“杀手”是 Stackless Python。它执行的某些任务是我当前使用的编译器所执行的任务,而这些是 Vyper 可能永远都做不到:它使“超轻量型线程”(由事件调度器驱动的合作多任务)的实现成为可能。 Vyper 是以 Ocaml 实现的,而后者使用机器堆栈;必须避免它,因为堆栈切换(用于同时从一个服务器处理许多客户机)非常昂贵。

Stackless Python:采访创始人 Christian Tismer

第一次见到 Stackless Python 时,它象是 CPython 的一个小支流。谈到编码,Stackless 对实际的 Python C 代码只做了小小更改(并重新定义“事实”)。不过,Christian Tismer( Stackless Python 的创始人)在 Stackless 中引入的概念非常深奥。它是“延续性”的概念(并且以 Python 对他们编程的方法)。

要尝试用最简单的术语解释它,延续性就是一种表示法,在程序中的特定点上,程序的每样事物都可以连续执行。延续性是依赖于初始条件的潜在。不是以传统方式进行循环,它可能使用不同的初始条件递归调用相同的持续性。我看到过的一种概括说法是,从理论上说,延续性更基本并位于 每个其它控制结构下。如果这些想法把您搞糊涂了,不必担心;这是正常的反应。

阅读 参考资料中 Tismer 的背景文章是进一步理解的良好开端。最好在阅读该文章后继续阅读他的参考资料。但对目前来说,让我们在更一般的级别上与 Tismer 进行交谈:

Mertz:Stackless Python 确切来说是什么?有什么初学者可以理解的概念能够解释有关 Stackless 的不同之处?
Tismer:Stackless Python 是一种不在 C 堆栈上保存状态的 Python 实现。它的确有堆栈 -- 要多少有多少 -- 但这些是 Python 堆栈。
C 堆栈不能从例如 C 这样的语言以干净的方式进行修改,除非以预期的顺序进行。它对您施加了很大限制:您必须回到这里,与您离开的方向正好相反。
“普通的”程序员一开始不会认为这是个限制。他们必须从开始就要学会将想法转向堆栈。堆栈没有什么坏处,并且通常它们施加的执行顺序就是实际上的顺序,但这并不意味着我们必须等待一个这样的堆栈序列完成才能运行另一序列。
程序员在必须执行非阻塞调用和回调时意识到这一点。堆栈突然挡住去路,我们必须使用线程,或将状态明确存储在对象中,或者构建显式的可切换堆栈等等。Stackless 的目的是帮助程序员避免这些问题。
Mertz:Stackless 的目标是达到与 CPython 100% 二进制兼容。是吗?
Tismer:Stackless 此刻就是 100% 的二进制兼容。这意味着:安装了 Python 1.5.2 后,用我的 Python15.dll 替换,每个文件仍能工作,包括每个扩展模块。这不是目标,而是要求,因为我不希望关注所有扩展。
Mertz:我认为 Stackless Python 绝对能吸引人们对它加以了解。和大多数讲实际的程序员一样,我在完全理解它时遇到了难题,但这是使它显得特别有趣的一部分。
Tismer:是啊,我也很讲实际,您可以想象在没有任何持续性概念,并且不知道在 Python 中它到底是什么样的情况下实现这样一件东西有多困难。无法想象某些事物,但要投入去做是我最大的挑战。完成后就很容易想象,也很容易重新设计。但在六个月的全职工作中,我猜有五个月的时间是在凝视屏幕和敲打键盘。
延续性很难销售。协同程序和生成器,特别是微线程还稍容易一点。所有以上产品都 可以在没有显式持续性的情况下实现。但有了持续性后,会发现转向其它结构所花费的步骤非常少,持续性就是使用的方法。所以我要改变我的营销策略,不再尝试销售持续性,而是它们的成果。对于能够看到光明的人来说,持续性仍然存在。
Mertz:有一个关于美国工程师和法国工程师的笑话。美国小组给法国小组带来一个原型。法国小组的反应是:“它在实践中工作得不错;但在理论上靠得住吗?”我想这个笑话可能是要嘲弄一下“法国人”的作风,但在我自己看来,我完全认同“法国人”的反应。排除笑话中任何针对特定民族的刻板模式,对它的认同是吸引我去了解 Stackless 的原因。CPython 在实践中使用,而 Stackless 在理论中使用!(换句话说,对于我个人,持续性的抽象纯粹性比微线程的上下文切换加速更有趣)。
Tismer:我也有类似的感觉。认识到 CPython 可以在不涉及 C 堆栈的情况下实现后,我确信它 必须用这种方式实现;其它所有方法都很荒唐。CPython 已经为框架对象付出了代价,但它使用 C 堆栈尝试它们更是丧失了所有自由。我觉得我必须解放 Python。:-)
我 1999 年 5 月开始这个项目。Sam Rushing 曾对硬件协同程序的实现有过粗略的研究,所以一场有关 Python 设备的讨论开始了。将堆栈复制做大量更改永远也不能将它变成 Python,这一点当时就很清楚。但可移植的、干净实现的协同程序可能可以。不幸的是,这是不可能的。五年前,Steve Majewski 在意识到如果不完全重写 Python 就无法解决这个问题之后就放弃了。
那就是挑战所在。我必须查明它。它如果是可能的,我就实现它;如果不可能,我就要证明这种不可能性。不久以后,经过了首次考虑和尝试后,Sam 告诉我有关 call/cc 以及它是如何强大。那时,我对它们在哪些方面比协同程序更强大一点概念也没有,但我相信他并实现了它们;六七次以后,总要完全重写,使我有了更深入的理解。
我希望最终能够以令人眼花缭乱的高速度创建线程,但我的初衷是发现究竟我能到达什么地步。
Mertz:在实践方面,Stackless 可能有哪些性能改进呢?这些改进在当前的实现中有多重要?它还有多少潜力可挖?哪些特定类型的应用程序最有可能从 Stackless 中受益?
Tismer:在当前的实现中,Stackless 比传统调用方案来说并不占太大的优势。普通的 Python 对新解释器开始递归。Stackless 展开到一个调度器,然后从那里启动解释器。这点几乎一样。实际的改进在于协同程序和线程的实现。它们需要由类模拟,或者需要是标准 Python 中的实际线程,但使用 Stackless,它们可以更直接地实现。
如果不对操作码集做巨大的更改,核心的更多改进就显得不太可能。但一个具有对其它地方延续性更多内置支持的再实现可以大大提高速度。
受益最大的那些特定应用程序可能是 Swarm 仿真,或有许多角色扮演极小任务的多人游戏。一例就是正在使用 Stackless Python 开发的 EVE 游戏(请参阅下面的 参考资料)。
Mertz:对于将 Stackless 合并到 CPython 主流您觉得如何?Stackless 是作为一个分支好呢,还是成为核心版本的话有些方面会更好?
Tismer:有一些支持和反对的争论。反对:只要我不放弃 Stackless 实现,它就是我的,我不需要讨论如何以及为什么的问题。但同时,我在尽力(但未能)追赶 CVS。最好让其它人来做。
其它不必对稀奇古怪的事物感兴趣的那些 Python 用户根本不会认识 Stackless;只是事实上它碰巧更快,最大递归级别现在是一个选项而不是硬件限制。对每个用户还有另一个保证:将有可腌制 (pickleable) 执行状态。这意味着可以在程序运行的时侯保存它,将它发送给朋友,然后继续运行。
最后,假使我的所有东西都变成核心,我就完全赞成;但我不希望看到一个就好象建议过多次的不完整的解决方案。
Mertz:对 Stackless 的未来方向有什么想法吗?将生产什么新的不同产品?Stackless 仍然受一些递归的困扰。它们会消失吗?
Tismer:将部分地实现腌制支持。首先对于微线程使用,因为现在它们提供最干净的概念。它们在“洁净室”里生活,这里不存在余下的递归问题。我的最终目标是从 Python 中除去 全部解释器递归。Stackless 的某些部分仍旧有递Stackless Python:采访创始人 Christian Tismer定义的 __xxx__ 方法。要定案很难,因为我们需要更改很多东西、添加新的操作码、展开某些内部调用序列等等。

相关主题

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Linux
ArticleID=52767
ArticleTitle=可爱的 Python:Python实现内幕
publish-date=10012000