Python是解释型的,还是编译型的,还是两者兼备?

286

据我所知:

解释性语言是高级语言,由解释器(一种将高级语言转换为机器代码并执行的程序)在运行时运行和执行;它逐步处理程序。

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

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

现在回到Python,我对此有点困惑。无论你在哪里学习Python,你都会了解到Python是一种解释性语言,但它被解释成某些中间代码(如字节码或IL),而不是机器代码。那么是哪个程序执行IM代码呢?请帮助我理解Python脚本的处理和运行方式。


3
可能是Is Python interpreted (like Javascript or PHP)?的重复问题。 - miku
2
Python在导入库时会创建.pyc文件(也称为字节码)。据我所知,字节码只能加快加载时间,而不能加快执行时间。 - Jesvin Jose
2
@aitchnyu:将字节码缓存到.pyc文件中确实只加速了加载,但这是因为Python代码在执行之前已被编译为字节码。虽然我认为尚未尝试过针对Python的特定情况,但其他语言实现表明,与纯AST或甚至未解析的源代码相比,字节码确实更容易高效地解释。例如,早期的Ruby版本从AST解释,而较新的版本则编译为字节码,并且据我所知,性能要好得多。 - user395760
1
@aitchnyu:我不知道你的意思。我只知道你的评论是正确的,而且为我们提供了一些背景信息,解释了为什么它只能加速加载时间,所以我决定补充那些信息。没有冒犯之意 :) - user395760
@Pankaj:关于你对“编译”的定义:有些程序不需要执行器。例如,C程序直接编译成机器码,可以直接执行。 - Josiah Yoder
显示剩余2条评论
15个回答

350
首先,解释/编译不是语言的属性,而是实现的属性。对于大多数语言,如果不是全部,大多数实现都属于一类,因此可以说该语言也是解释/编译的,但这仍然是一个重要的区别,因为它有助于理解,并且有相当多的语言具有可用的两种实现(主要是函数式语言领域,例如Haskell和ML)。此外,还有C解释器和试图将Python的子集编译成C或C++代码(随后编译为机器代码)的项目。
其次,编译不仅限于提前编译为本地机器代码。编译器更普遍地是指将一种编程语言中的程序转换为另一种编程语言的程序的程序(可以说,即使应用了重大变换,您甚至可以拥有相同输入和输出语言的编译器)。JIT编译器在运行时编译为本地机器代码,这可以提供非常接近或甚至优于提前编译的速度(取决于基准测试和比较实现的质量)。

但是停止挑剔,回答你想问的问题:实际上(即使用一种相对流行和成熟的实现),Python是编译的。不是提前编译成机器码(即按照受限制和错误的,但常见的定义“编译”),而是“仅”编译为字节码,但它仍然具有至少一些好处的编译。例如,语句a = b.c()被编译为一个字节流,当“反汇编”时,看起来有点像load 0 (b); load_str 'c'; get_attr; call_function 0; store 1 (a)。这是一个简化,实际上更难读懂且更低级 - 你可以尝试使用标准库{{link2:dis模块}}并查看真正的情况。从较高级别的表示中解释这比解释快。

这个字节码可以被解释执行(请注意,直接解释和首先编译成某种中间表示再解释之间在理论上和实际性能上有所不同),就像参考实现(CPython)一样,或者在运行时既被解释执行又被编译成优化的机器代码,就像PyPy一样。


3
好的,这意味着Python脚本首先被编译成字节码,然后由解释器(如CPython、Jython或IronPython等)执行。 - Pankaj Upadhyay
42
不,它被编译成字节码,然后由相应的虚拟机执行。CPython既是编译器又是虚拟机,但Jython和IronPython只是编译器。 - Ignacio Vazquez-Abrams
1
@Igacio:我对IronPython/Jython没有太多经验,但至少Jython提供了类似解释器的层。我不认为将Python转换为静态类型的JVM字节码是可行的。不过,你提到编译器和解释器是同一包的好点子。 - user395760
2
@delnan:嗯,Jython 确实充当了 Python 语言和 Java VM 之间的一种中介,但它确实编译成了 Java 字节码。 - Ignacio Vazquez-Abrams
1
首先,解释/编译不是语言的属性,而是实现的属性。我认为这并不正确。解释型语言可以执行动态语句,即源代码在程序编写时甚至在应用程序启动时都不知道。这不是实现细节,而是语言设计的一部分。 - user2555515
显示剩余7条评论

65

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

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


27
我很喜欢关于“需要另一个程序来运行它”的观点,这有助于澄清我的一些想法。 - Matt
2
所以python.exe首先优化代码,然后再解释执行吗? - Koray Tugay
4
当python.exe获得可读的文本源代码时,它首先生成优化的字节码,然后解释执行(就像你所说的一样);但是,当已经有一个字节码文件(预编译的)时,它不必执行第一步,这样可以节省一些时间。 - GordonBGood
这就是为什么你总是需要另一个程序来运行Python脚本。当你说这个的时候,你是在谈论解释器(将源代码转换为字节码)还是虚拟机(执行字节码)?如果我错了,请纠正我。 - qwerty_url

49
答案取决于使用的Python实现。如果你正在使用CPython(Python的标准实现)或Jython(专为与Java编程语言集成而设计),它首先会被翻译成字节码,然后根据你所使用的Python实现,将此字节码 传送到相应的虚拟机进行解释。对于CPython来说是PVM(Python虚拟机),对于Jython来说是JVM(Java虚拟机)。
但是,假设你正在使用另一个标准的CPython实现——PyPy,它会使用即时编译器(Just-In-Time Compiler)。

1
在转换为字节码时,确实需要编译器,那么它是哪一个? - Ritesh
10
Pypy是一种Python实现,不是"CPython"的实现。事实上,Pypy是CPython的一个替代品(https://www.pypy.org/features.html)。 - Giorgio

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

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

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

...

由于没有编译步骤…

...

Python解释器和丰富的标准库可供使用...

...

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

32

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

看看它如何同时具备编译和解释:

首先我想告诉你,如果你来自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 进行解释。

现在让我们稍微谈一下Java和Python中即时编译器的作用

在JVM中,存在Java解释器,它逐行解释字节码以获取本地机器码进行执行,但是当Java字节码由解释器执行时,执行速度总是更慢。那么解决方案是什么?解决方案是即时编译器,它生成本地代码,可以比解释器快得多地执行。一些JVM供应商使用Java解释器,而另一些则使用即时编译器。参考:点击这里 在Python中,为了绕过解释器实现快速执行,可以使用其他Python实现(PyPy)代替CPython点击这里 查看包括PyPy在内的其他Python实现。

25

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

当我们指示Python运行脚本时,在我们的代码实际开始执行之前,Python会执行一些步骤:

  • 将其编译为字节码。
  • 然后将其发送到虚拟机。

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

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

实际上,Python通过将每个源代码语句分解为单个步骤,并将其转换为字节码指令来翻译源代码的每个语句。字节码翻译是为了加快执行速度。

字节码可以比原始源代码语句快得多地运行。它具有.pyc扩展名,如果可以写入我们的计算机,它将被写入。

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

如果Python无法将字节码文件写入我们的计算机,程序仍然可以工作。字节码是在内存中生成的,程序退出时被简单地丢弃。但是,由于.pyc文件可以加速启动时间,我们可能需要确保它已经被写入较大的程序中。

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

字节码文件也是提供Python代码的一种方式。即使原始的.py源文件不存在,如果Python找不到其他文件并且只能找到.pyc文件,Python仍将运行程序。

Python虚拟机(PVM)

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


我认为你需要强调的重要部分是,Python像PHP、JSP和CFML一样,在运行时进行编译。它不像Java或C#那样预先编译。 这使得开发人员可以进行更改并将更改上传到服务器,就这样。我们在进行更改后不需要手动构建任何东西。 - Charles Robertson

7

如果(您了解Java){

Python代码与Java一样转换为字节码。
每次尝试访问它时,都会重新执行该字节码。

}否则{

Python代码最初被翻译成称为字节码的东西,非常接近机器语言但不是实际的机器代码,因此每次访问或运行它时,都会再次执行该字节码。

}


嗯,一切最终都会转化为字节码。 区分语言的重要部分是这个过程发生的时间。 在Python中,就像PHP、JSP和CFML一样,这个过程发生在运行时之前。它由应用服务器执行。 而像Java和C#这样的语言需要开发人员在每次更改后手动“构建”应用程序。构建过程将代码预编译为字节码。据我的经验,这样可以更快地运行,但这是一个谬论。 - Charles Robertson

5

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

每次运行程序时,都会执行该字节码。怎么做?当我们运行程序时,将该字节码(位于.pyc文件中)作为输入传递给1虚拟机(VM) - 运行时引擎,允许我们的程序执行-然后执行它。

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

说明:

1计算机系统仿真

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

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

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


3

对于新手

在运行Python脚本之前,它会自动将脚本编译成字节码,也就是所谓的编译代码。

运行脚本并不被视为导入操作,因此不会创建 .pyc 文件。

例如,如果你有一个名为 abc.py 的脚本文件,其中导入了另一个模块 xyz.py,在运行 abc.py 时,由于导入了 xyz 模块,xyz.pyc 将被创建,但由于未导入 abc.py,因此不会创建 abc.pyc 文件。

原始答案: "最初的回答"


2
几乎可以说Python是一门解释型语言。但是我们在Python中使用了部分一次性编译过程,将完整的源代码转换为字节码,就像Java语言一样。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接