作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
查尔斯·马什的头像

查尔斯·马什

查理(BCS, 普林斯顿大学)一直是可汗学院的工程领导, 然后雪松, 现在在Spring Discovery做ML.

Share

Python 是惊人的.

令人惊讶的是,这是一个相当模糊的说法. 我说的“Python”是什么意思?? 我指的是抽象的Python吗 接口? 我指的是CPython,普通的Python吗 实现 (不要与同名的Cython混淆)? 或者我说的完全是别的意思? 也许我间接地指的是Jython、IronPython或PyPy. 或者也许我真的走得太远了,我在谈论RPython或rubpython(它们非常, 非常不同的东西).

而上面提到的技术是通用命名和通用引用的, 其中一些服务于完全不同的目的, 至少, 以完全不同的方式操作).

在我使用Python接口的整个过程中,我遇到了大量这样的接口 .* ython工具. 但直到最近,我才花时间去了解它们是什么, 它们是如何工作的, 为什么他们是必要的(以他们自己的方式).

在本教程中, 我将从头开始介绍各种Python实现, 最后是对PyPy的全面介绍, 我相信这是语言的未来.

这一切都始于对“Python”到底是什么的理解.

如果您对机器代码、虚拟机等有很好的理解,可以随意地 跳过.

Python是解释的还是编译的?”

这是的公点 Python初学者的困惑.

在进行比较时,首先要意识到的是“Python”是一个 接口. 有一个 规范 Python的什么 should 怎么做? should 行为(与任何接口一样). 有很多 实现 (与任何接口一样).

第二件要意识到的事情是“解释”和“编译”是an的属性 实现,不是。 接口.

所以这个问题本身并不是很好.

Python是解释的还是编译的? 这个问题不是很好.

也就是说, 对于最常见的Python实现(CPython:用C编写), 通常简称为“Python”, 当然,如果你不知道我在说什么,你在用什么), 答案是: 解释, with some 编译. CPython 编译* Python源代码到字节码,然后 解释 这个字节码,在它运行时执行它.

* 注意:这不是传统意义上的“编译”. 通常, 我们会说“编译”就是把一种高级语言转换成机器代码. 但它是某种“汇编”.

让我们更仔细地看看这个答案, 因为它将帮助我们理解后面的一些概念.

字节码和. 机器代码

理解字节码和字节码之间的区别非常重要. 机器代码(又名本机代码),也许最好的例子说明:

  • C编译成机器码,然后直接在处理器上运行. 每条指令都指示CPU移动数据.
  • Java编译成字节码, 然后在Java虚拟机(JVM)上运行, 执行程序的计算机的抽象概念. 每个指令然后由JVM处理,JVM与您的计算机交互.

简单地说: 机器码要快得多,但字节码更易于移植和安全.

机器代码根据您的机器看起来不同,但字节码在所有机器上看起来都是一样的. 有人可能会说机器代码是 优化 到你的设置.

回到CPython实现,工具链流程如下:

  1. CPython将Python源代码编译成字节码.
  2. 该字节码随后在CPython虚拟机上执行.
初学者通常认为Python被编译是因为 .佩克文件. 这话有一定道理: .Pyc文件是编译后的字节码,然后对其进行解释. 如果你之前运行过Python代码 .如果Pyc文件方便的话,它第二次运行的速度会更快,因为它不需要重新编译字节码.

可选虚拟机:Jython、IronPython等

正如我前面提到的,Python有 several 实现. Again, 如前所述, 最常见的是CPython, 但是为了这个比较指南,还有其他一些应该提到. 这是一个用C编写的Python实现,被认为是“默认”实现.

但是其他的Python实现呢? 其中一个比较突出的是 Jython一种利用JVM编写Java的Python实现. 当CPython生成在CPython VM上运行的字节码时,Jython生成 Java字节码 在JVM上运行(这与编译Java程序时生成的东西是一样的).

这个Python实现图描述了Jython对Java字节码的使用.

“为什么要使用替代实现呢?你可能会问. 首先,这些 不同的Python实现可以很好地使用不同的技术栈.

CPython使为Python代码编写C扩展变得非常容易,因为最终它由C解释器执行. 另一方面,Jython使与其他Java程序一起工作变得非常容易:您可以导入 any Java类,不需要额外的工作, 从Jython程序中调用并使用Java类. (旁白:如果你没有仔细考虑过,这实际上是疯狂的. 我们正处于这样一个阶段,你可以混合和混合不同的语言,并将它们编译成相同的内容. (正如 Rostin在美国,混合了Fortran和C代码的程序已经出现一段时间了. 所以,当然,这并不一定是新的. 但它仍然很酷.))

作为示例,这是有效的Jython代码:

[Java HotSpot(TM) 64位服务器VM (Apple Inc .).)].6.0_51
>>> from java.导入HashSet
>>> s = HashSet(5)
>>> s.add (" Foo ")
>>> s.add(“酒吧”)
>>> s
(Foo, Bar)

IronPython 是另一个流行的Python实现,完全用c#编写,目标是 .网络堆栈. 特别地,它运行在 .. NET虚拟机,微软的 公共语言运行时(CLR),与JVM相当.

你可能会这么说 Jython: Java: IronPython: c#. 它们运行在相同的虚拟机上, 你可以从IronPython代码中导入c#类,从Jython代码中导入Java类, etc.

完全有可能在不接触非cpython Python实现的情况下存活下来. 但是转换也有好处, 其中大部分依赖于你的技术栈. 使用大量基于jvm的语言? Jython可能适合您. 关于 .网络堆栈? 也许你应该尝试一下IronPython(也许你已经尝试过了).

这个Python比较图表展示了Python实现之间的差异.

顺便说一下,虽然这不是 reason 使用不同的实现, 请注意,这些实现在处理Python源代码的方式之外,实际上在行为上有所不同. However, 这些差异通常很小, 并且随着时间的推移,随着这些实现处于积极的开发中而消失或出现. 例如,IronPython 默认情况下使用Unicode字符串; CPython, however, 默认为ASCII 对于版本2.x(对于非ascii字符会出现UnicodeEncodeError错误),但不支持 默认为3的Unicode字符串.x.

即时编译:PyPy和未来

所以我们有一个用C编写的Python实现,一个用Java编写,一个用c#编写. 下一个合乎逻辑的步骤:用…… Python. (受过教育的读者会注意到这有点误导.)

这就是事情可能变得混乱的地方. 首先,让我们讨论一下即时(JIT)编译.

JIT: Why and How

回想一下,本机机器码比字节码快得多. 如果我们可以编译一些字节码然后作为本机代码运行呢? 我们必须付出一些代价来编译字节码(例如.e.但如果最终结果更快,那就太好了! 这就是JIT编译的动机, 一种混合了解释器和编译器优点的技术. 简而言之,JIT希望利用编译来加快解释系统的速度.

例如,jit采用的一种常见方法是:

  1. 标识频繁执行的字节码.
  2. 将其编译为本机机器码.
  3. 缓存结果.
  4. 每当设置要运行相同的字节码时, 相反,获取预编译的机器码并从中获益.e.(加速).

这就是PyPy实现的全部内容:将JIT引入Python(参见 Appendix 感谢之前的努力). 有, 当然, 其他目标:PyPy的目标是跨平台, memory-light, 和stackless-supportive. 但JIT才是它真正的卖点. 作为一堆时间测试的平均值,据说它将性能提高了一个因子 6.27. 有关详细信息,请参见 PyPy速度中心:

使用PyPy实现将JIT引入Python接口,在性能改进方面获得了回报.

PyPy很难理解

PyPy有巨大的潜力,在这一点上 高度兼容的 使用CPython(所以 它可以运行Flask, Django, etc.).

但是围绕PyPy有很多困惑(例如,请参阅创建一个 PyPyPy…). 在我看来,这主要是因为PyPy实际上是两件事:

  1. Python解释器 RPython (不是Python(我之前撒了谎)). RPython是Python的一个具有静态类型的子集. 在Python中,它是 “几乎不可能” 对类型进行严格的推理(为什么这么难? 我们考虑一下这个事实:

     X =随机的.选择([1," foo "])
    

    将是有效的Python代码(归功于 Ademan). 是什么类型的 x? 当变量的类型没有被严格强制时,我们怎么能对变量的类型进行推理呢?). 与RPython, 你牺牲了一些灵活性, 而是让它变得更多, 更容易理解内存管理之类的东西, 哪一个允许优化.

  2. 一个编译器,为各种目标编译RPython代码并添加JIT. 默认平台是C, i.e.这是一个RPython-to-C编译器,但您也可以针对JVM和其他编译器.

仅为了清晰起见,在本Python比较指南中,我将它们称为PyPy(1)和PyPy(2)。.

为什么你需要这两样东西,为什么在同一屋檐下? 这样想:PyPy(1)是一个用RPython编写的解释器. 所以它接收用户的Python代码并将其编译成字节码. 但是解释器本身(用RPython编写)必须由另一个Python实现解释才能运行, right?

我们可以 使用CPython的 运行解释器. 但这不会很快.

Instead, 我们的想法是使用PyPy(2)(称为RPython工具链)将PyPy的解释器编译成另一个平台的代码.g.(C、JVM或CLI)在我们的机器上运行,也添加了JIT. 这很神奇:PyPy动态地将JIT添加到解释器中,生成自己的编译器! (再一次,这是疯狂的:我们正在编译一个解释器,添加另一个独立的编译器.)

最后, 结果是一个独立的可执行文件,它解释Python源代码并利用JIT优化. 这正是我们想要的! 这有点拗口,但也许这个图表会有所帮助:

该图说明了PyPy实现的美妙之处, 包括翻译, 编译器, 以及带有JIT的可执行文件.

再次重申, PyPy的真正美妙之处在于,我们可以在RPython中编写一堆不同的Python解释器,而不用担心JIT. 然后,PyPy将为我们实现JIT 使用RPython工具链/PyPy (2).

事实上,如果我们再抽象一点,理论上你可以为 any 语言,将其提供给PyPy,并获得该语言的JIT. 这是因为PyPy专注于优化实际的解释器, 而不是它所解释的语言的细节.

理论上,你可以为任何语言编写解释器, 喂给PyPy, 并获得该语言的JIT.

作为一个简短的题外话,我想提一下JIT本身是绝对迷人的. 它使用一种称为跟踪的技术,该技术执行 如下:

  1. 运行解释器并解释所有内容(不添加JIT).
  2. 对解释后的代码做一些简单的分析.
  3. 确定您以前执行过的操作.
  4. 把这些代码编译成机器码.

更多信息, 这篇论文 是非常容易理解和有趣的吗.

总结一下:我们使用PyPy的RPython-to-C(或其他目标平台)编译器来编译PyPy的rpython实现的解释器.

结束

在对Python实现进行了长时间的比较之后,我不得不问自己:为什么这这么好? 为什么这个疯狂的想法值得追求呢? I think 亚历克斯·盖纳 把它放在他的身上 blog“(PyPy是未来),因为(它)提供了更快的速度, 更大的灵活性, 并且是Python发展的更好平台.”

简而言之:

  • 它很快,因为它将源代码编译为本机代码 (使用JIT).
  • 它是灵活的,因为它将JIT添加到解释器中 只需要很少的额外工作.
  • 它很灵活(再次),因为你可以在RPython中编写解释器,它比C更容易扩展(实际上,它非常简单,以至于有一个 编写自己的解释器的教程).

附录:你可能听说过的其他Python名称

  • Python 3000 (Py3k): Python 3的另一种命名.主修,主修; 向后不兼容的 Python的发行版 2008. Py3k团队预测,这需要大约 五年 这个新版本要被完全采用. 虽然 most (警告:坊间传言)Python开发人员继续使用Python 2.x,人们越来越意识到Py3k.

  • CythonPython的超集,包含调用C函数的绑定.
    • 目标:允许你为Python代码编写C扩展.
    • 还可以添加 静态类型 到现有的Python代码,允许它被编译并达到类似c的性能.
    • 这与PyPy类似,但又不相同. 在这种情况下,您在将用户代码传递给编译器之前强制在其中键入类型. 使用PyPy,您可以编写普通的老式Python,编译器会处理任何优化.

  • Numba:一个“即时专门化编译器”,将JIT添加到 带注释的 Python代码. 用最基本的术语来说,您给它一些提示,它就会加快部分代码的速度. Numba是…的一部分 水蟒 分发,一组用于数据分析和管理的软件包.

  • IPython与其他讨论的内容非常不同. Python的计算环境. 交互式,支持GUI工具包和浏览器体验等.

  • Psyco: a Python扩展模块,也是Python JIT的早期成果之一. 然而,它后来被标记为 “无人维护和死亡”. 事实上,Psyco的首席开发者, Armin Rigo现在在PyPy工作.

Python语言绑定

  • RubyPython: Ruby和Python虚拟机之间的桥梁. 允许您将Python代码嵌入到Ruby代码中. 您定义Python的启动和停止位置,然后rubypypython在vm之间封送数据.

  • PyObjc: Python和Objective-C之间的语言绑定,作为它们之间的桥梁. 几乎, 这意味着你可以从Python代码中利用Objective-C库(包括创建OS X应用程序所需的一切), 和Python模块从你的Objective-C代码. 在这种情况下,用C编写CPython很方便,C是Objective-C的子集.

  • PyQtPyObjc给你绑定OS X GUI组件, PyQt为Qt应用程序框架做同样的事情, 让您创建丰富的图形界面, 访问SQL数据库, etc. 另一个旨在将Python的简单性带给其他人的工具 框架.

JavaScript框架

  • pyjs(睡衣):一个用Python创建web和桌面应用程序的框架. 包括一个Python-to-JavaScript编译器、一个小部件集和一些其他工具.

  • Brython:一个用JavaScript编写的Python VM,允许在浏览器中执行Py3k代码.

就这一主题咨询作者或专家.
预约电话

世界级的文章,每周发一次.

订阅意味着同意我们的 隐私政策

世界级的文章,每周发一次.

订阅意味着同意我们的 隐私政策

Toptal开发者

加入总冠军® 社区.