【问题标题】:Is Python interpreted, or compiled, or both?Python 是解释的,还是编译的,或者两者兼而有之?
【发布时间】:2011-10-16 21:55:18
【问题描述】:

据我了解:

解释型语言是一种高级语言,由解释器(将高级语言转换为机器代码然后执行的程序)在运行中运行和执行;它一次处理一点程序。

已编译语言是一种高级语言,其代码首先由编译器(将高级语言转换为机器代码的程序)转换为机器代码,然后由executor(另一个运行代码的程序)。

如果我的定义有误,请纠正我。

现在回到 Python,我对此感到有些困惑。在任何地方,您都知道 Python 是一种解释型语言,但它被解释为一些中间代码(如字节码或 IL),而 不是 为机器代码。那么哪个程序会执行 IM 代码呢?请帮助我了解如何处理和运行 Python 脚本。

【问题讨论】:

  • 无论何时导入库,Python 都会创建 .pyc 文件(所谓的字节码)。 AFAIK 字节码只能加快加载时间,而不是执行时间。
  • @aitchnyu:在 .pyc 文件中缓存字节码只会加快加载速度,但前提是 Python 代码在执行之前被编译为字节码。虽然我不认为它已经专门用 Python 进行过尝试,但其他语言实现表明字节码确实比普通的 AST 更容易有效地解释,或者更糟糕的是,未解析的源代码。例如,较旧的 Ruby 版本从 AST 解释,AFAIK 的性能优于编译为字节码的较新版本。
  • 不想听起来粗鲁,但这不是我的意思吗(但不像你那么有见识)?
  • @aitchnyu:我不明白你的意思。我只知道您的评论没有错误,但为一些背景信息提供了很好的机会为什么它只会加快加载时间,所以我决定添加这些信息。无意冒犯或采取冒犯:)

标签: python interpreted-language


【解决方案1】:

首先,解释/编译不是语言的属性,而是实现的属性。对于大多数语言,如果不是所有的实现都属于一个类别,那么可能会省下几句话来说明该语言也被解释/编译,但这仍然是一个重要的区别,因为它有助于理解并且因为有很多语言具有两种可用的实现(主要在函数式语言领域,请参阅 Haskell 和 ML)。此外,还有一些 C 解释器和项目试图将 Python 的子集编译为 C 或 C++ 代码(随后编译为机器代码)。

其次,编译不限于提前编译为本机代码。更一般地说,编译器是将一种编程语言的程序转换为另一种编程语言的程序的程序(可以说,如果应用了重要的转换,您甚至可以拥有具有相同输入和输出语言的编译器)。 JIT 编译器在运行时编译为本机机器代码,这可以提供非常接近甚至优于提前编译的速度(取决于基准和比较的实现质量)。

但要停止吹毛求疵并回答您要问的问题:实际上(阅读:使用某种流行且成熟的实现),Python 是编译的。未提前编译为机器代码(即“编译”受限制和错误,但可惜的是通用定义),“仅”编译为bytecode,但它仍然是至少有一些好处的编译。例如,语句a = b.c() 被编译为一个字节流,当“反汇编”时,它看起来有点像load 0 (b); load_str 'c'; get_attr; call_function 0; store 1 (a)。这是一种简化,实际上它的可读性更低,而且更底层——您可以使用标准库dis module 进行试验,看看真正的交易是什么样的。解释这一点比从更高级别的表示中解释要快。

该字节码要么被解释(请注意,在理论和实际性能上,直接解释和首先编译为某种中间表示并解释它之间存在差异),就像参考实现 (CPython) 一样,或者两者都被解释并在运行时编译为优化的机器代码,如PyPy

【讨论】:

  • 好吧,这意味着python脚本首先被编译成字节码,然后由CPython、Jython或IronPython等解释器实现。
  • @Pankaj:确实。原则上,Python 与 Java 和各种 .NET 语言一样是一种编译语言;)(特别是因为 PyPy 提供了一个实用的 JIT 编译器。)
  • 不,它被编译成字节码,然后字节码由各自的VM执行。 CPython 既是编译器又是 VM,但 Jython 和 IronPython 只是编译器。
  • @Igacio:我对 IronPython/Jython 没有太多经验,但至少 Jython 不提供类似解释器的层吗?我认为尝试将 Python 转换为静态类型的 JVM 字节码是不可行的。不过,编译器和解释器是同一个包的一部分,这一点很好。
  • @delnan:嗯,Jython 确实充当了 Python 语言和 Java VM 之间的一种中介,但它确实编译为 Java 字节码。
【解决方案2】:

CPU 确实只能理解机器码。对于解释程序,解释器的最终目标是将程序代码“解释”成机器代码。然而,现代解释语言通常不会直接解释人类代码,因为它效率太低。

Python 解释器首先读取人类代码并将其优化为一些中间代码,然后再将其解释为机器代码。这就是为什么您总是需要另一个程序来运行 Python 脚本的原因,这与在 C++ 中您可以直接运行代码的编译可执行文件不同。例如,c:\Python27\python.exe/usr/bin/python

【讨论】:

  • 我喜欢“需要另一个程序来运行 [it]”的观点。这有助于澄清我的一些想法。
  • 所以python.exe先优化代码再解释?
  • @KorayTugay,当 python.exe 被赋予人类可读的文本源代码时,它首先生成优化的字节码,然后解释它(如你所说);但是,当已经有字节码文件(预编译)时,就不必做第一步了,这样可以节省一些时间。
  • > 这就是为什么你总是需要另一个程序来运行 Python 脚本当你这么说的时候,你是在谈论解释器(将源代码转换为字节码)还是虚拟机(执行字节码)代码)?如果我错了,请纠正我
【解决方案3】:

答案取决于所使用的 python 实现。如果您使用的是 CPython(python 的标准实现)或 Jython(针对与 java 编程语言集成),它首先被翻译成 字节码,并且根据你使用的python的实现,这个bycode被定向到相应的虚拟机进行解释PVM(Python 虚拟机)用于 CPython,JVM(Java 虚拟机)用于 Jython。

但是假设您正在使用 PyPy,这是另一种标准的 CPython 实现。它将使用即时编译器

【讨论】:

  • 在翻译成字节码的过程中,它确实需要一个编译器,那是哪一个?
  • Pypy 是一个 Python 实现,而不是“CPython”实现。事实上,Pypy 是 CPython 的替代品 (pypy.org/features.html)。
【解决方案4】:

根据官方 Python 网站,它是被解释的。

https://www.python.org/doc/essays/blurb/

Python 是一种解释型、面向对象的高级编程语言...

...

由于没有编译步骤...

...

Python 解释器和广泛的标准库可用...

...

相反,当解释器发现错误时,它会引发 例外。当程序没有捕捉到异常时, 解释器打印堆栈跟踪。

【讨论】:

    【解决方案5】:

    是的,它既是编译语言又是解释语言。 那为什么我们一般称它为解释语言呢?

    看看它是如何编译和解释的?

    首先我想告诉你,如果你来自 Java 世界,你会更喜欢我的回答。

    在 Java 中,源代码首先通过 javac 编译器转换为字节码,然后定向到 JVM(负责生成用于执行目的的本机代码)。现在我想向您展示我们将 Java 称为编译语言,因为我们可以看到它确实编译了源代码并通过以下方式提供 .class 文件(除了字节码):

    javac Hello.java -------> 生成 Hello.class 文件

    java Hello -------->将字节码定向到 JVM 用于执行目的

    python 也会发生同样的事情,即首先源代码通过编译器转换为字节码,然后定向到 PVM(负责生成用于执行目的的本机代码)。现在我想向您展示我们通常将 Python 称为解释型语言,因为编译发生在幕后 当我们通过以下方式运行python代码时:

    python Hello.py --------> 直接执行代码,如果代码语法正确,我们可以看到输出

    @ python Hello.py 看起来像直接执行,但实际上它首先生成由解释器解释的字节码以生成用于执行目的的本机代码。

    CPython- 负责编译和解释。

    如果您需要更多详细信息,请查看以下几行

    正如我提到的,CPython 编译源代码,但实际编译是在 cython 的帮助下进行的,然后在 CPython

    的帮助下进行解释

    现在让我们稍微谈谈 Just-In-Time 编译器在 Java 和 Python 中的作用

    在 JVM 中存在 Java 解释器,它逐行解释字节码以获取用于执行目的的本机机器代码,但是当解释器执行 Java 字节码时,执行总是会变慢。那么解决方案是什么?解决方案是 Just-In-Time 编译器,它生成的本机代码执行速度比解释速度快得多。一些 JVM 供应商使用 Java Interpreter,一些使用 Just-In-Time 编译器。参考:click here

    在 python 中绕过解释器以实现快速执行,请使用另一个 python 实现 (PyPy) 而不是 CPythonclick here 用于 python 的其他实现,包括 PyPy

    【讨论】:

      【解决方案6】:

      对于刚开始使用 python 的人来说,这是一个很大的困惑,这里的答案有点难以理解,所以我会让它更容易。

      当我们指示 Python 运行我们的脚本时,Python 会在我们的代码真正开始运行之前执行几个步骤:

      • 它被编译成字节码。
      • 然后路由到虚拟机。

      当我们执行一些源代码时,Python 会将其编译成字节码。编译是一个翻译步骤,字节码是源代码的低级平台无关表示。

      请注意,Python 字节码不是二进制机器码(例如,英特尔芯片的指令)。

      实际上,Python 通过将源代码的每个语句分解为单独的步骤,将它们转换为字节码指令。执行字节码翻译以加快执行速度。 字节码可以比原始源代码语句运行得更快。它有.pyc扩展名,如果它可以写入我们的机器,它将被写入。

      因此,下次我们运行相同的程序时,Python 将加载 .pyc 文件并跳过编译步骤,除非它已被更改。 Python 会自动检查源代码和字节码文件的时间戳,以了解何时必须重新编译。如果我们重新保存源代码,下次运行程序时会自动重新创建字节码。

      如果 Python 无法将字节码文件写入我们的机器,我们的程序仍然可以工作。字节码在内存中生成,并在程序退出时被丢弃。但由于 .pyc 文件加快了启动时间,我们可能希望确保它是为更大的程序编写的。

      让我们总结一下幕后发生的事情。 当 Python 执行程序时,Python 将 .py 读入内存,并对其进行解析以获取字节码,然后继续执行。对于程序导入的每个模块,Python 首先检查是否存在预编译的字节码版本,在 .pyo 或 .pyc 中,其时间戳对应于其 .py 文件。 Python 使用字节码版本(如果有)。否则,它会解析模块的 .py 文件,将其保存到 .pyc 文件中,并使用它刚刚创建的字节码。

      字节码文件也是传送 Python 代码的一种方式。如果 Python 只能找到 .pyc 文件,它仍然会运行程序,即使原始 .py 源文件不存在。

      Python 虚拟机 (PVM)

      一旦我们的程序被编译成字节码,它就会被运送到 Python 虚拟机 (PVM) 中执行。 PVM 不是一个单独的程序。它不需要自行安装。实际上,PVM 只是一个大循环,一个一个地遍历我们的字节码指令,以执行它们的操作。 PVM 是 Python 的运行时引擎。它始终作为 Python 系统的一部分存在。它是真正运行我们脚本的组件。从技术上讲,这只是所谓的 Python 解释器的最后一步。

      【讨论】:

        【解决方案7】:

        如果(你懂 Java){

        Python 代码像 java 一样被转换成字节码。
        每次您尝试访问该字节码时都会再次执行该字节码。

        } else {

        Python 代码最初被翻译成一种叫做 bytecode
        的东西 接近机器语言但不是实际的机器代码
        所以每次我们访问或运行它时,字节码都会再次执行

        }

        【讨论】:

          【解决方案8】:

          这真的取决于所使用的语言的实现!但是,在任何实现中都有一个共同的步骤:您的代码首先被编译(翻译)为中间代码 - 您的代码和机器(二进制)代码之间的东西 - 称为 bytecode (存储到 .pyc 文件中) .请注意,这是一次性步骤,除非您修改代码,否则不会重复。

          并且每次运行程序时都会执行该字节码。如何?好吧,当我们运行程序时,这个字节码(在 .pyc 文件中)作为输入传递给 虚拟机 (VM)1 - 运行时引擎允许我们的程序被执行 - 执行它。

          根据语言实现,VM 将解释字节码(在 CPython2 实现的情况下)或 JIT-compile3 它(在PyPy4 实现)。

          注意事项

          1计算机系统的仿真

          2 字节码解释器;该语言的参考实现,用 C 和 Python 编写 - 使用最广泛

          3 在程序执行期间(运行时)进行的编译

          4 字节码 JIT 编译器; CPython 的替代实现,用 RPython(受限 Python)编写 - 通常比 CPython 运行得更快

          【讨论】:

            【解决方案9】:

            几乎,我们可以说 Python 是解释型语言。但是我们在python中使用了一次性编译过程的一部分,将完整的源代码转换成类似java语言的字节码。

            【讨论】:

              【解决方案10】:

              对于新手

              Python 会在运行之前自动将您的脚本编译为已编译的代码,即所谓的字节码。

              运行脚本不被视为导入,不会创建 .pyc。

              例如,如果你有一个脚本文件abc.py,它导入了另一个模块xyz.py,当你运行abc.py时,由于导入了xyz,所以会创建xyz.pyc,但不会创建abc.pyc文件因为 abc.py 没有被导入。

              【讨论】:

                【解决方案11】:

                Python(解释器)被编译

                证明:如果包含语法错误,它甚至不会编译您的代码。

                示例 1:

                print("This should print") 
                a = 9/0 
                

                输出:

                This should print
                Traceback (most recent call last):
                  File "p.py", line 2, in <module>
                    a = 9/0
                ZeroDivisionError: integer division or modulo by zero
                

                代码编译成功。第一行被执行(print)第二行抛出ZeroDivisionError(运行时错误)。

                示例 2:

                print("This should not print")
                /0         
                

                输出:

                  File "p.py", line 2
                    /0
                    ^
                SyntaxError: invalid syntax
                

                结论:如果您的代码文件包含SyntaxError,则编译失败不会执行任何操作。

                【讨论】:

                • 如果你尝试 print("this should print"); a=9/0;那么你就会得到真正的答案。
                【解决方案12】:

                您编写的 python 代码被编译成 python 字节码,它创建扩展名为 .pyc 的文件。如果编译,另一个问题是,为什么不编译语言。

                请注意,这不是传统意义上的编译。通常,我们会说编译是采用高级语言并将其转换为机器代码。但它是各种各样的汇编。编译成中间代码而不是机器代码(希望你现在明白了)。

                回到执行过程,你的字节码,存在于 pyc 文件中,在编译步骤中创建,然后由适当的虚拟机执行,在我们的例子中,CPython VM 时间戳(称为幻数)用于验证 .py 文件是否已更改,具体取决于创建的新 pyc 文件。如果 pyc 是当前代码,那么它只是跳过编译步骤。

                【讨论】:

                  【解决方案13】:

                  正如有人已经说过的那样,“解释/编译不是语言的属性,而是实现的属性。” Python 可用于解释模式和编译模式。当您直接从终端或 cmd 运行 python 代码时,python 解释器就会启动。现在,如果您编写任何命令,则该命令将被直接解释。如果您使用包含 Python 代码的文件并在 IDE 中运行它或使用命令提示符,它将首先编译整个代码,然后将其转换为字节码然后运行。所以这取决于我们如何使用它。

                  【讨论】:

                    【解决方案14】:

                    似乎是语义的一个案例。我认为我们大多数人推断编译的通常结果是机器代码。考虑到这一点,我对自己说 python 没有被编译。我错了,因为编译确实意味着转换为较低级别,所以从源代码转换为字节码也是编译。

                    【讨论】:

                    • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
                    【解决方案15】:

                    在我看来,Python 被归为解释器类别,因为它被设计为能够完全处理(从 python 代码到在 cpu 中执行)单个 python 语句。 IE。你写一个语句,你可以执行它,如果没有错误则得到相应的结果。

                    拥有一个中间代码(如字节码)我相信将其总体归类为编译器并没有什么不同。虽然这个组件(中间代码生成)通常是编译器的一部分,但它也可以在解释器中使用。参见解释器https://en.m.wikipedia.org/wiki/Interpreter_(computing) 的维基定义。它是提高执行速度效率的关键部分。有了缓存,它的功能就更强大了,如果您没有更改当前程序范围内的代码,您可以跳过繁重的处理步骤,例如词法、语义分析甚至一些代码优化。

                    【讨论】:

                      猜你喜欢
                      • 2016-09-22
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2016-09-22
                      • 2014-07-20
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多