【问题标题】:Why compile Python code?为什么要编译 Python 代码?
【发布时间】:2010-10-03 00:58:02
【问题描述】:

为什么要编译 Python 脚本?您可以直接从 .py 文件运行它们,它工作正常,那么有性能优势还是什么?

我还注意到我的应用程序中的一些文件被编译为 .pyc 而其他文件没有,这是为什么呢?

【问题讨论】:

  • 您可能还注意到,如果您的代码是公司机密,您不能共享代码,包括更快地启动您的应用程序,您也会获得安全性。
  • @PSyLoCKe 你真的,真的不知道。 Python 字节码是真正可读的,因为编译器不需要对其进行混淆来优化它。 (并不是说它优化了很多......)
  • 一些文件被自动编译的原因是因为它们是被导入的;例如,如果您使用import mylib.py,Python 将编译mylib.py,以便将来的import 语句运行得更快一些。如果您稍后更改mylib.py,那么它将在下次导入时重新编译(Python 使用文件日期来查看是否发生这种情况。)

标签: python compilation


【解决方案1】:

它被编译成字节码,可以使用得更快、更快。

某些文件未编译的原因是您使用python main.py 调用的主脚本在您每次运行该脚本时都会重新编译。所有导入的脚本都将被编译并存储在磁盘上。

Ben Blank的重要补充:

值得注意的是,在运行 编译后的脚本有更快的启动 时间(因为它不需要 编译),它不会运行任何 更快。

【讨论】:

  • 值得注意的是,虽然运行已编译的脚本有更快的启动时间(因为它不需要编译),但它不会运行 更快。​​
  • 一个常见的误解。感谢分享。
  • 除了不需要编译之外,.pyc 文件几乎总是更小。特别是如果你评论很多。我的一个是 28419 作为 .py,但只有 17879 作为 .pyc - 所以加载时间也更好。最后,您可以通过这种方式预编译顶级脚本:python -m compileall myscript.py
  • 内存消耗有区别吗?我正在基于 mips cpu 的嵌入式设备上测试 Python,只有 64MB 的 RAM,那么在启动 Python 脚本的编译版本时,内存使用有什么优势吗?
  • @valentt:可能不会。我对 Python 内部结构了解不多,但我认为在 Python 中解析为字节码不会占用大量内存。我想不出需要大量记忆才能记住某些状态的东西。
【解决方案2】:

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

“Python 简介”says 这是关于编译的 Python 文件的:

程序在以下情况下不会运行得更快 它是从“.pyc”或“.pyo”中读取的 文件比从“.py”读取时 文件;唯一更快的 关于“.pyc”或“.pyo”文件是 它们的加载速度。

运行 .pyc 文件的优点是 Python 不必在运行之前产生编译它的开销。由于 Python 无论如何都会在运行 .py 文件之前编译为字节码,因此除此之外不应该有任何性能改进。

使用已编译的 .pyc 文件可以获得多少改进?这取决于脚本的作用。对于仅打印“Hello World”的非常简短的脚本,编译可能占总启动和运行时间的很大一部分。但是对于运行时间较长的脚本,相对于总运行时间而言,编译脚本的成本会降低。

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

【讨论】:

  • 在很多情况下很难看出区别,但我有一个超过 300,000 行的特定 python 文件。 (是另一个脚本生成的一堆数学计算,用于测试)编译需要37秒,执行只需2秒。
【解决方案3】:

优点:

首先:温和的、可克服的混淆。

第二:如果编译后的文件明显更小,您将获得更快的加载时间。非常适合网络。

第三:Python可以跳过编译步骤。在初始负载下更快。非常适合 CPU 和网络。

第四:评论越多,.pyc.pyo 文件与源.py 文件相比就越小。

第五:手头只有.pyc.pyo 文件的最终用户不太可能向您展示由于他们忘记告诉您的未还原更改而导致的错误。

第六:如果你的目标是嵌入式系统,获得更小的尺寸 要嵌入的文件可能是一个显着的优点,而且架构是稳定的,所以下面详述的缺点之一不会发挥作用。

顶级编译

知道您可以通过这种方式将顶级 python 源文件编译成.pyc 文件是很有用的:

python -m py_compile myscript.py

这会删除 cmets。它使docstrings 完好无损。如果您也想摆脱docstrings(您可能要认真考虑为什么要这样做),那么请改用这种方式编译...

python -OO -m py_compile myscript.py

...你会得到一个.pyo 文件而不是.pyc 文件;在代码的基本功能方面同样可分配,但比剥离的 docstrings 的大小更小(如果它首先具有体面的 docstrings,那么以后的工作就不太容易理解)。但请参阅下面的缺点三。

请注意,python 使用 .py 文件的日期(如果存在)来决定它是否应该执行 .py 文件而不是 .pyc.pyo 文件 --- 所以编辑你的 .py文件,而 .pyc.pyo 已过时,您获得的任何好处都将丢失。您需要重新编译它才能再次获得 .pyc.pyo 的好处,就像他们可能的那样。

缺点:

首先:.pyc.pyo 文件中有一个“magic cookie”,它指示编译 python 文件的系统架构。如果将其中一个文件分发到不同类型的环境中,它将休息。如果您分发 .pyc.pyo 而没有关联的 .py 重新编译或 touch 以取代 .pyc.pyo,最终用户也无法修复它。

第二:如果docstrings 如上所述使用-OO 命令行选项跳过,则没有人能够获得该信息,这会使代码的使用更加困难(或不可能。 )

第三:Python的-OO选项也按照-O命令行选项实现了一些优化;这可能会导致操作发生变化。已知的优化有:

  • sys.flags.optimize = 1
  • assert 语句被跳过
  • __debug__ = 错误

第四:如果你故意让你的 python 脚本在第一行使用#!/usr/bin/python 的顺序执行,这将在.pyc.pyo 文件中被删除,并且该功能将丢失。

第五:有些明显,但如果你编译你的代码,不仅会影响它的使用,而且其他人从你的工作中学习的可能性也会降低,通常会很严重。

【讨论】:

    【解决方案4】:

    没有提到的是source-to-source-compiling。例如,nuitka 将 Python 代码转换为 C/C++,并将其编译为直接在 CPU 上运行的二进制代码,而不是运行在速度较慢的虚拟机上的 Python 字节码。

    这可以显着提高速度,或者在您的环境依赖于 C/C++ 代码时让您使用 Python。

    【讨论】:

      【解决方案5】:

      运行已编译的 python 的性能有所提高。但是当你将 .py 文件作为导入模块运行时,python 会编译并存储它,只要 .py 文件没有改变,它就会一直使用编译后的版本。

      在使用文件时,使用任何相互干扰的语言,过程看起来像这样:
      1.文件由interpeter处理。
      2. 文件编译
      3.编译后的代码被执行。

      显然,通过使用预编译代码,您可以省去第 2 步,这适用于 python、PHP 等。

      这是一篇有趣的博客文章,解释了这些差异http://julipedia.blogspot.com/2004/07/compiled-vs-interpreted-languages.html
      这是一个解释Python编译过程的条目http://effbot.org/zone/python-compile.htm

      【讨论】:

        【解决方案6】:

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

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

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

        【讨论】:

        • 关于保护您的代码 - 编译不会有很大帮助。编译混淆 - 但无论如何,有愿望的人都会得到你的代码。
        • @josh 总是有可能的,如果一个人可以访问内存或观看 CPU 的指令,有足够的时间,他们会重新构建您的应用程序。
        • 同意,但是正如 Unkwntech 所说,如果这个人足够坚定,这将永远是可能的。但我相信这在大多数情况下就足够了,你通常只想限制人们“修复”你的代码......
        • 编译成字节码的语言通常对reverse-compile 来说并不是那么那么难,除非你采取额外的步骤来混淆它们——仅仅编译通常是不够的。
        【解决方案7】:

        运行已编译的脚本时肯定会存在性能差异。如果你运行正常的.py 脚本,机器每次运行都会编译它,这需要时间。在现代机器上,这几乎不会引起注意,但随着脚本的增长,它可能会成为一个更大的问题。

        【讨论】:

          【解决方案8】:

          我们使用编译后的代码分发给无权访问源代码的用户。基本上是为了阻止没有经验的程序员在不告诉我们的情况下意外更改某些内容或修复错误。

          【讨论】:

            【解决方案9】:

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

            如果您的某些文件没有被编译,可能 Python 无法写入 .pyc 文件,可能是因为目录权限或其他原因。或者未编译的文件可能永远不会被加载......(脚本/模块只有在它们第一次加载时才会被编译)

            【讨论】:

              【解决方案10】:

              初学者假设 Python 是因为 .pyc 文件而编译的。 .pyc 文件是编译后的字节码,然后会被解释。因此,如果您之前运行过 Python 代码并且手边有 .pyc 文件,那么第二次运行速度会更快,因为它不必重新编译字节码

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

              口译员: 解释器还将高级语言转换为机器可读的二进制等价物。每当解释器获得要执行的高级语言代码时,它都会先将代码转换为中间代码,然后再将其转换为机器代码。代码的每一部分都被解释,然后按顺序单独执行,如果在代码的一部分中发现错误,它将停止代码的解释,而不翻译下一组代码。

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

              【讨论】:

              • 您对“编译器”的定义不正确。编译器从来没有被编译成机器代码。编译器只是从一种语言到另一种语言的翻译器。这就是为什么我们说 Python “编译”为字节码,Coffeescript “编译”为 Javascript,等等。
              猜你喜欢
              • 2014-05-24
              • 1970-01-01
              • 2012-08-04
              • 2012-12-28
              • 1970-01-01
              • 1970-01-01
              • 2016-11-23
              • 1970-01-01
              • 2017-01-11
              相关资源
              最近更新 更多