为什么Python不需要编译器?


29

只是想知道(既然我已经从需要编译器的C ++开始),为什么Python不需要编译器?

我只需输入代码,将其另存为exec,然后运行即可。在C ++中,我必须进行构建以及所有其他有趣的东西。


4
Python只是一种具有许多实现的语言。Iron Python的编译方式与C#和C ++的编译方式相同,并且可能还有其他类似的实现方式。
工作

1
C#和C ++的编译方式不同-尽管您可能会争辩说它们最终都将最终以机器指令的形式出现,但是如果您这样做,则可以说BASIC也以相同的方式编译。
gbjbaanb 2012年

7
@gbjbaanb,但随后再次未编译英语,并且对一个句子的语义分析可能会产生两个同等有效的结果,并且以上内容可以理解为“像C#和C ++一样编译Iron python”
Rune FS

您使用什么平台/软件编写Python代码?如果编写.py文件,则该文件不是可执行文件。它仍然是源代码文件。在命令行中,您使用python命令来解释.py文件,或者,如果您使用IDLE或Eclipse,则IDE会为您执行。
里克·亨德森

Answers:


68

Python有一个编译器!您只是没有注意到它,因为它会自动运行。但是,您可以知道它在那里:查看为您的模块生成的.pyc(或.pyo是否已打开优化器)文件import

另外,它不会编译为本机计算机的代码。而是将其编译为虚拟机使用的字节码。虚拟机本身就是一个编译程序。这与Java的工作原理非常相似。实际上非常相似,实际上有一个Python变体(Jython)可以编译为Java虚拟机的字节码!还有IronPython,可编译为Microsoft的CLR(由.NET使用)。(普通的Python字节码编译器有时也称为CPython,以使它们与这些替代方案毫无歧义。)

C ++需要公开其编译过程,因为该语言本身并不完整。它没有指定链接器构建程序所需了解的所有内容,也没有可移植地指定编译选项(某些编译器允许您使用#pragma,但这不是标准的)。因此,您必须使用makefiles以及自动地狱(autoconf / automake / libtool)完成其余工作。这实际上只是C语言所做的保留。C之所以这样做,是因为它使编译器变得简单,这是它如此流行的一个主要原因(任何人都可以在80年代开发出一个简单的C编译器)。


一些可能影响编译器或链接器操作但在C或C ++语法中未指定的东西:

  • 依赖解决
  • 外部库要求(包括依赖关系顺序)
  • 优化器级别
  • 警告设置
  • 语言规范版本
  • 链接器映射(在最终程序的哪个部分)
  • 目标架构

其中一些可以被检测到,但无法指定。例如,我可以检测到正在使用哪个C ++ __cplusplus,但是我无法在代码本身中指定C ++ 98是用于我的代码的那个。我必须将其作为标志传递给Makefile中的编译器,或者在对话框中进行设置。

虽然您可能会认为编译器中存在一个“依赖关系解决方案”系统,该系统会自动生成依赖关系记录,但这些记录仅说明给定源文件使用哪些头文件。它们无法指示链接到可执行程序所需的其他附加源代码模块,因为在C或C ++中没有标准的方法来指示给定的头文件是另一个源代码模块的接口定义,而不是仅仅一堆。您希望在多个地方显示的行,因此您不会重复自己。文件命名约定中有一些传统,但是编译器和链接器不知道或不执行这些传统。

可以使用设置其中几个#pragma,但这不是标准的,我所说的是标准。所有这些东西可以由一个标准指定,但并不是为了向后兼容。普遍的看法是,makefile和IDE不会损坏,因此请不要修复它们。

Python用语言来处理所有这些。例如,import指定显式的模块依赖关系,表示依赖关系树,并且模块不拆分为头文件和源文件(即接口和实现)。


3
Python的C实现是CPythonCython则有所不同。
Greg Hewgill'2

4
C编译为机器代码的其他原因是,它的目的仅仅是一个出色的汇编程序,因为字节码解释器在其所拥有的硬件上在技术上是不可行的,并且因为最重要的任务之一是编写OS内核。
tdammers

2
@BillyONeal有一个很大的例外,在c / c ++中,您作为程序员必须以某种方式(在Python中将makefile或将所有内容转储到同一blob中)进行处理,而您只需完成工作,然后将编译器与VM一起进行即可负责其余部分
符文FS

3
“ C ++需要公开其编译过程,因为语言本身是不完整的”,什么?
与莫妮卡(Monica)进行的轻度比赛

3
你读的部分之后的权利,对不对?“它没有指定链接器构建程序所需了解的所有内容,也没有可移植地指定编译选项。” 您不能仅仅通过将其提供给编译器来构建任何 C ++文件。通常,您必须提供元数据,例如编译标志,包含路径等。此元数据不是标准指定的,也不是可移植的,这就是为什么我们必须将其他内容(例如make,cmake,Visual Studio或其他内容)拖到其他位置的原因完成工作。因此,该标准必须在编译单元中调用某些内容,而在程序范围内则需要调用其他内容。
Mike DeSimone 2015年

7

Python是一种解释型语言。这意味着您的计算机上存在可以读取Python代码并将“指令”发送到计算机的软件。在上解释语言维基百科文章可能会感兴趣。

编译诸如C ++(一种编译语言)之类的语言时,意味着将其转换为机器代码,以便在执行时直接由硬件读取。在上编译语言维基百科文章可能提供一个有趣的对比。


21
没有解释或编译语言之类的东西。语言是一组抽象的数学规则。语言不会被编译或解释。一种语言就是。编译和解释是编译器或解释器(duh!)的特征,而不是语言的特征。每种语言都可以用编译器实现,每种语言都可以用解释器实现。大多数语言都具有编译和解释的实现。有C ++解释器和Python编译器。(实际上,当前所有现有的Python实现都具有编译器。)
JörgW Mittag'2

4
大多数现代高性能语言实现都将解释器和编译器(或什至几个编译器)结合在一起,以实现最佳性能。实际上,没有解释器就不可能运行任何程序。毕竟,编译器只是将程序从一种语言翻译成另一种语言的程序。但是在某些时候,您必须实际运行该程序,该程序由解释器完成(可能会或可能不会在硅中实现)。
约尔格W¯¯米塔格

10
@JörgWMittag:技术上您是正确的。但是,大多数语言是为解释性上下文或完整编译而设计的。为GW BASIC或Common Lisp编写解释器比为C ++或C#编写解释器容易得多。如果没有交互环境,Python将失去许多卖点。为PHP编写编译器非常困难,而且效率极低,因为由于eval()和类似的构造,编译后的可执行文件必须包含整个PHP解释器-有人可能会说这样的编译器会作弊。
tdammers

2
@tdammers,是的。我们可以合理地使用“编译语言”来表示“通常编译的语言”。但这遗漏了将PHP,Java,Python,Lua和C#都实现为字节码编译器的观点。所有这些语言还为它们实现了JIT。因此,实际上,您不能真正调用其中某些语言进行编译和解释,因为它们具有相同的实现策略。
温斯顿·埃韦特'02

2
@BillyONeal,至少对于python不是这样。您可以分发python字节码并在没有源代码的情况下运行它。但是的确,没有编译器就无法分发python。
温斯顿·埃韦特'02

5

并非所有的编译语言都有一个面对面的edit-compile-link-run循环。

您遇到的是C ++的功能/局限性(或至少是C ++的实现)。

要执行任何操作,您必须将代码存储到文件中,并通过称为链接的过程构建整体图像。

特别是,这种整体式链接过程被误认为是编译和解释之间的区别。

有些语言通过消除笨拙的整体链接步骤,而不是通过消除对机器代码的编译,来更加动态地完成所有这些工作。源代码仍被编译为目标文件,但它们被加载到运行时映像中,而不是链接到整体可执行文件中。

您说“重新加载此模块”,然后它会加载源代码并对其进行解释或编译,具体取决于某些模式切换。

即使您使用的是C语言,Linux内核编程也具有这种风格。您可以重新编译模块并进行加载和卸载。当然,您仍然知道您正在生成一些可执行的东西,并且它由复杂的构建系统管理,还需要一些手动步骤。但是事实是,最后您可以卸载和重新加载那个小模块,而不必重新启动整个内核。

某些语言的模块化程度甚至更高,并且构建和加载均在其运行时完成,因此更加无缝。


2

从最初的问题转移了什么……没有提到的一点是,python程序的源代码是您使用和分发的,从用户的角度来看,它就是程序。我们倾向于将事物简化为定义不明确的类别。

通常认为编译程序是机器代码的独立文件。(当然,通常包含指向与特定操作系统相关联的动态链接库的链接)。这就是说...大多数编程语言都有变种,可以描述为已编译或解释。

Python不需要编译器,因为它依赖于可编译和运行代码的应用程序(称为解释器),而无需以易于访问或分发的形式存储所创建的机器代码。


1

所有编程语言都需要将人的概念转换为目标机器代码。甚至汇编语言也必须翻译成机器代码。该翻译通常在以下阶段进行:

阶段1:分析和转换(解析)为中间代码。阶段2:使用占位符将外部代码转换为目标机器代码。阶段3:解析外部引用并将其打包到机器可执行程序中。

这种转换通常称为预编译和“及时”(JIT)或运行时编译。

诸如C,C ++,COBOL,Fortran,Pascal(并非全部)和Assembly之类的语言是预编译的语言,可以由操作系统直接执行而无需解释器。

诸如Java,BASIC,C#和Python之类的语言将被解释。他们都使用在阶段1中创建的中间代码,但是有时将它们转换为机器代码的方式有所不同。最简单的形式使用该中间代码来执行可以完成预期工作的机器代码例程。其他人则将中间代码编译为机器代码,并在运行时进行外部依赖关系修复。编译后即可立即执行。同样,机器代码存储在先前编译的可重用机器代码的缓存中,如果以后需要再次使用该功能,则以后可以重用。如果一个函数已经被缓存,则解释器不需要再次编译它。

大多数现代高级语言都属于解释型(使用JIT)类别。预编译的大多是较旧的语言,例如C&C ++。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.