为什么要编译Python代码?

297

为什么要编译 Python 脚本?直接从 .py 文件运行脚本也能正常工作,那么是否有性能优势或其他好处呢?

我还注意到在我的应用程序中,某些文件被编译成 .pyc 文件,而另一些则没有,这是为什么?


您还可以注意到,除了应用程序启动更快之外,如果您不能分享代码因为它是公司机密,您还可以在安全方面获得收益。 - NaN
2
@PSyLoCKe,你真的不需要这样做。Python字节码非常易读,因为编译器不需要混淆它来进行优化。(虽然它并没有进行太多优化...) - wizzwizz4
2
一些文件自动编译的原因是因为它们被导入了;例如,如果你使用 import mylib.py,Python 将会编译 mylib.py,以便未来的 import 语句运行更快。如果你稍后更改了 mylib.py,那么下次导入它时它将被重新编译(Python 使用文件日期来实现这一点)。 - fyngyrz
10个回答

325

这些代码被编译成字节码,可以运行得非常快。

之所以有些文件没有被编译,是因为主脚本(用 python main.py 调用的脚本)每次运行时都会被重新编译。所有导入的脚本将会被编译并存储在硬盘上。

Ben Blank 的重要补充:

值得注意的是,尽管运行已编译的脚本具有更快的启动时间(因为它不需要编译),但它并没有运行得更


332
值得注意的是,运行编译后的脚本具有更快的启动时间(因为它不需要编译),但它并没有运行得更快。 - Ben Blank
3
除了不需要编译之外,.pyc 文件几乎总是更小的。特别是如果你有很多注释的话。我的一个 .py 文件大小为 28419,但是 .pyc 文件只有 17879 —— 因此加载时间也更快。最后,你可以通过以下方式预编译顶层脚本:python -m compileall myscript.py。 - fyngyrz
2
内存消耗有什么区别吗?我正在测试基于 MIPS CPU 的嵌入式设备上的 Python,只有 64MB 的 RAM,因此在启动编译后的 Python 脚本时,在内存使用方面是否有任何优势? - valentt
1
@valentt:可能不需要。我对Python内部了解不多,但我认为在Python中将解析转换为字节码不需要太多的内存。我想不出需要大量内存来记住某些状态的情况。 - Georg Schölly
1
在我看来,这个回答的第一句话有些误导性,因此应该首先提到这个“Important addition”。 - benjaminz
显示剩余5条评论

100

.pyc文件是已经编译成字节码的Python文件。如果Python找到与您调用的.py文件同名的.pyc文件,则会自动运行该.pyc文件。

《Python简介》中说道编译的Python文件:

从“.pyc”或“.pyo”文件读取代码并不比从“.py”文件中读取代码运行更快;唯一更快的地方是加载速度。

运行.pyc文件的优点在于Python在运行之前无需编译它,因此可以减少开销。由于Python在运行.py文件之前会编译为字节码,因此除此之外不应有任何性能提升。

使用编译的.pyc文件可以获得多少性能提升?这取决于脚本的功能。对于一个简单打印“Hello World”的脚本,编译可能占据总启动和运行时间的大部分时间。但是对于运行时间较长的脚本,相对于总运行时间,编译脚本的成本将降低。

命令行上命名的脚本永远不会保存到.pyc文件中。只有被“主”脚本加载的模块才会以这种方式保存。


8
很多时候难以看出区别,但我有一个特定的Python文件,包含超过30万行代码(这是由另一个用于测试的脚本生成的数学计算),编译需要37秒,但只需2秒就能执行。 - wojtow

74

优点:

第一:轻微、可消除的混淆。

第二:如果编译结果文件明显变小,则可以获得更快的加载时间。对于网页来说非常不错。

第三:Python 可以跳过编译步骤,初始化加载更快。对于 CPU 和网页来说很好。

第四:你越是注释,与源代码相比,.pyc.pyo 文件就会变得越小。

第五:仅具有 .pyc.pyo 文件的最终用户,很少会因为未还原的更改而向您提出由他们引起的错误。

第六:如果你的目标是嵌入式系统,获得较小的嵌入式文件大小可能代表着一个重要的优势,并且体系结构稳定,因此下面详述的缺点并不会发生。

顶层编译

知道这种方法可以将顶层 Python 源代码文件编译成 .pyc 文件也是很有用的:

python -m py_compile myscript.py

这将删除注释。它会保留文档字符串。如果您也想删除文档字符串(您可能需要认真考虑为什么要这样做),那么请改用编译方法...

python -OO -m py_compile myscript.py

如果你使用了-OO命令行选项,你将会得到一个.pyo文件而不是.pyc文件;这两者在代码基本功能方面相同,但docstrings被删除后文件大小更小(如果一开始有良好的docstrings则在以后的使用中可读性会降低)。但请参见以下第三个缺点。

请注意,Python会使用.py文件的日期(如果存在)来决定是否应该执行.py文件而不是执行.pyc.pyo文件 --- 因此如果修改了.py文件,与之关联的.pyc.pyo文件将过时,失去优化效果。您需要重新编译才能再次获得.pyc.pyo的优化效果,如果可能的话。

缺点:

第一:在.pyc.pyo文件中有一个“魔法cookie”,它指示Python文件编译所在的系统架构。如果将其中一个这样的文件分发到不同类型的环境中,它将无法使用。如果将.pyc.pyo文件分发而不附带相关的.py文件以重新编译或触摸它,使其取代.pyc.pyo文件,则最终用户也无法修复它。

第二:如果使用上述-OO命令行选项跳过docstrings,则没有人能够访问该信息,这可能会导致使用该代码更加困难(甚至是不可能)。

第三:Python的-OO选项还实现了一些优化,如-O命令行选项;这可能会导致操作变化。已知的优化包括:

  • sys.flags.optimize = 1
  • 跳过assert语句
  • __debug__ = False

第四:如果您有意将Python脚本执行文件与类似#!/usr/bin/python的内容放在第一行,那么这将被剥离在.pyc.pyo文件中,从而失去该功能。

第五:使用选项-O-OOassert语句都不会被编译进去,消除了运行时验证的来源。您可以通过使用try except来补偿,但这需要放弃assert语句在编译中使用。

第六:有点显然,但如果您编译代码,不仅可能会影响其使用,而且其他人从您的工作中学习的潜力也会大大降低。


你评论得越多,代码就变得越小;)) - code
一句口头语。 ;) 有罪。 - fyngyrz

19

还有一件没有提到的事情是源代码编译。例如,nuitka将Python代码转换成C/C++,并将其编译为可以直接运行在CPU上的二进制代码,而不是运行在较慢的虚拟机上的Python字节码。

这可以显著加速或让你在依赖于C/C++代码的环境中使用Python。


11

编译过的Python代码运行速度更快。然而,当你将一个.py文件作为导入模块运行时,Python会将其编译和存储起来,只要.py文件没有改变,Python就会一直使用已编译的版本。

对于任何解释型语言,当文件被使用时,这个过程看起来像这样:
1. 文件被解释器处理。
2. 文件被编译。
3. 编译后的代码被执行。

显然,通过使用预编译代码,可以消除第2步,这同样适用于Python、PHP等其他语言。

这是一个有趣的博客文章,解释了编译和解释语言之间的区别http://julipedia.blogspot.com/2004/07/compiled-vs-interpreted-languages.html
这里有一个条目解释了Python的编译过程http://effbot.org/zone/python-compile.htm


9

如前所述,将您的Python代码编译为字节码可以提高性能。这通常由Python自身处理,仅适用于导入的脚本。

您可能希望编译Python代码的另一个原因是保护您的知识产权不被复制和/或修改。

您可以在Python文档中了解更多信息。


3
关于保护你的代码,编译并不能提供太多帮助。编译会使代码混淆,但是有意愿的人仍然能够获取你的代码。 - Josh Smeaton
1
@josh 这总是有可能的,如果某人可以访问内存或监视 CPU 的指令,在足够的时间和意愿下,他们可以重构你的应用程序。 - UnkwnTech
5
同意,但是像Unkwntech所说的那样,如果一个人足够决心,那么总是有可能的。但我相信在大多数情况下它已经足够了,通常你只是想限制人们"修复"你的代码... - Simon B. Jensen
2
编译为字节码的语言通常并不难于进行“反编译”(除非你采取了额外的混淆步骤)---仅仅编译通常是不够的。 - EJoshuaS - Stand with Ukraine

7

运行编译过的脚本确实会有性能差异。如果你运行普通的.py脚本,机器每次运行时都会进行编译,这需要时间。在现代计算机上,这几乎不可察觉,但随着脚本规模的增大,可能会成为更大的问题。


4

我们使用编译后的代码分发给没有源代码访问权限的用户。这么做主要是为了防止经验不足的程序员无意中更改或修复错误而没有告知我们。


2

是的,性能是主要原因,据我所知,也是唯一的原因。

如果您的某些文件没有被编译,则可能是Python无法写入.pyc文件,可能是由于目录权限或其他原因。或者未编译的文件根本没有被加载...(只有在首次加载时才会编译脚本/模块)


1

初学者可能会认为Python是编译的,因为有 .pyc 文件。.pyc文件是已编译的字节码,然后被解释执行。因此,如果之前运行过python代码并且有 .pyc 文件,第二次运行时会更快,因为无需重新编译字节码。

编译器: 编译器是一种将高级语言翻译成机器语言的代码。

解释器: 解释器也将高级语言转换成可读的二进制相当于机器语言。每次解释器获取要执行的高级语言代码时,都会将代码转换为一个中间代码,再将其转换为机器代码。代码的每个部分都会被解释并按顺序单独执行,如果在代码的某个部分发现错误,则会停止代码的解释而不转换下一组代码。

Sources: http://www.toptal.com/python/why-are-there-so-many-pythons http://www.engineersgarage.com/contribution/difference-between-compiler-and-interpreter


15
你对“编译器”的定义是错误的。编译器从来都不是只能编译成机器码。编译器只是一种将一种语言翻译成另一种语言的工具。这就是为什么我们说Python“编译”成字节码,CoffeeScript“编译”成JavaScript等等。 - Ricky Stewart
我总是使用术语“编译”来指代将文本翻译成较低级别的语言,而将“转译”用于类似级别的语言之间的翻译。 - quoniam
我总是使用术语“编译”来指代将文本翻译成较低级别语言的过程,而将“转译”用于相似级别语言之间的翻译。 - undefined

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