【问题标题】:Python interpretation model in comparison to direct and virtual machine compilationPython 解释模型与直接编译和虚拟机编译的比较
【发布时间】:2012-06-29 18:47:09
【问题描述】:

我一直compiling diagrams(双关语)希望了解常见编程语言的不同实现。我了解代码是编译还是解释取决于代码的实现,而不是编程语言本身的一个方面。

我有兴趣将 Python 解释与直接编译(C++ 除外)进行比较

和虚拟机模型(例如 Java 或 C#)

根据上面的这两个图,请你帮我开发一个类似的流程图,说明如何将 .py 文件转换为 .pyc,使用标准库(我认为它们称为模块)然后实际运行。 SO 上的许多程序员表示,作为脚本语言的 python 不是由 CPU 执行,而是由解释器执行,但这听起来很不可能,因为最终必须由硬件进行计算。

【问题讨论】:

  • 您的图表不够详细,无法显示 Python 和 Java 之间的区别。只需将 .java 替换为 .py 并将 .class 替换为 .pyc。
  • @cababunga 我明白了,所以有 Python 编译器和 Python 虚拟机?

标签: python compiler-construction scripting interpreter vm-implementation


【解决方案1】:

首先,这是一个实现细节。我将我的答案限制在 CPython 和 PyPy 上,因为我熟悉它们。 Jython、IronPython 和其他实现的答案会有所不同 - 可能完全不同。

Python 更接近于“虚拟机模型”。 Python 代码与一些对他们的知识水平来说太大声的人的陈述相反,尽管每个人(包括我)在随意的讨论中都将它混为一谈,但从未被解释过。它在加载时总是编译为字节码(同样,在 CPython 和 PyPy 上)。如果它是因为模块被导入并从 .py 文件加载而被加载的,则可能会创建一个 .pyc 文件来缓存编译输出。此步骤不是强制性的;您可以通过各种方式将其关闭,并且程序执行不会受到丝毫影响(除非下一个加载模块的过程必须再次执行)。但是,编译成字节码是不可避免的,如果不是从磁盘加载,字节码是在内存中生成的。

然后在模块级别执行此字节码(其确切细节是实现细节并且在版本之间有所不同),这需要构建函数对象、类对象等。这些对象只是重用(持有指向)已经在内存中的字节码。这与 C++ 和 Java 不同,其中代码和类在编译期间/之后是一成不变的。在执行过程中,可能会遇到import 语句。我缺乏篇幅、时间和理解来描述进口机械,但简短的故事是:

  • 如果它已经被导入一次,您将获得该模块对象(静态语言仅在编译时具有的另一个运行时构造)。在任何 Python 代码运行之前,已经导入了几个内置模块(好吧,所有这些模块都在 PyPy 中,原因超出了这个问题的范围),这仅仅是因为它们与解释器的核心紧密集成并且非常基础。 sys 就是这样一个模块。一些 Python 代码也可以预先运行,尤其是在您启动交互式解释器时(查找 site.py)。
  • 否则,模块被定位。这方面的规则不是我们关心的。最后,这些规则到达 Python 文件或动态链接的机器代码片段(Windows 上的 .DLL,虽然 Python 模块专门使用扩展名 .pyd 但这只是一个名称;在 unix 上使用等效的 .so )。
    • 模块首先加载到内存中(动态加载,或解析并编译为字节码)。
    • 然后,模块被初始化。扩展模块对被调用的对象有一个特殊的功能。 Python 模块只是从上到下运行。在表现良好的模块中,这只是设置全局数据、定义函数和类以及导入依赖项。当然,其他任何事情也可能发生。生成的模块对象被缓存(记住第一步)并返回。

所有这些都适用于标准库模块以及第三方模块。这也是为什么如果您调用您的脚本就像您在该脚本中导入的标准库模块一样调用您的脚本(它会自行导入,尽管不会由于缓存而崩溃 - 这是我忽略的许多事情之一),您可能会收到一条令人困惑的错误消息。

字节码的执行方式(问题的最后一部分)不同。 CPython 只是解释它,但正如您正确指出的那样,这并不意味着它神奇地不使用 CPU。取而代之的是一个大而丑陋的循环,它检测接下来应该执行什么字节码指令,然后跳转到一些执行该指令语义的本机代码。 PyPy 更有趣;它开始解释但沿途记录一些统计数据。当它认为值得这样做时,它会开始详细记录解释器所做的事情,并生成一些高度优化的本机代码。解释器仍然用于 Python 代码的其他部分。请注意,许多 JVM 和可能的 .NET 也是如此,但您引用的图表掩盖了这一点。

【讨论】:

    【解决方案2】:

    python的参考实现:

    (.py) -> python (检查 .pyc) -> (.pyc) -> python (执行动态加载模块)

    other implementations。最值得注意的是:

    • jython 将 (.py) 编译为 (.class) 并从那里遵循 java 模式
    • pypy 在编译 (.py) 时使用 JIT。那里的链可能会有所不同(pypy 可以在 cpython、jython 或 .net 环境中运行)

    【讨论】:

    • PyPy 如何“将 .py 编译为更多的 .py”?就 .py 和 .pyc 文件而言,它的作用与 CPython 几乎完全相同。
    • @delnan 链接解释但它使用 JIT。运行速度比 CPython (speed.pypy.org) 快。使用大量的黑暗魔法。查看stackoverflow.com/questions/2591879/…
    • 我知道 JIT 以及生成它的翻译工具链,几乎可以不用涉足源代码。但是,JIT 不会将 .py 文件编译为 .py 文件,甚至不会在您讨论的阶段开始跟踪。
    • (后退一步)你是对的,它不会创建另一个(.py)。我不是它的用户,因为它对我来说太年轻了。只是想强调正在使用的其他实现对您的问题有不同的答案。
    • (1) 这不是我的问题。其实是我自己回答的。 (2) 我也不把它用于严肃的用途,尽管只是因为兼容性(Python 3 和可能的一些库)。 (3) 概述它是一个实现细节是好的,但写废话不是。你为什么不删除那部分?
    【解决方案3】:

    Python 在技术上是一种脚本语言,但它也是经过编译的,python 源代码从其源文件中获取并输入解释器,解释器通常在内部将源代码编译为字节码,然后将其丢弃或在外部将其保存为 . pyc

    是的,python 是一个单独的虚拟机,然后位于实际硬件之上,但所有 python 字节码都是 pvm(python 虚拟机)的一系列指令,就像实际 CPU 的汇编程序一样。

    【讨论】:

    • 关于你的最后一点,pvm 是否像 jvm 一样为 cpu 输出本机机器代码?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-11
    • 1970-01-01
    • 2011-03-18
    • 2014-12-09
    • 1970-01-01
    • 2011-01-26
    相关资源
    最近更新 更多