关于编译:如果解释Python,什么是.pyc文件?

If Python is interpreted, what are .pyc files?

我被赋予理解python是一种解释性语言…但是,当我查看python源代码时,我看到了.pyc文件,Windows将其标识为"编译后的python文件"。这些在哪里?


I've been given to understand that
Python is an interpreted language...

Ok.

这种流行的模因是不正确的,或者说,是建立在对(自然)语言水平的误解之上的:类似的错误是说"圣经是一本精装书"。我来解释一下这个比喻…好的。

"圣经"是"一本书",是指一类(实际的,实物的)书籍;被认为是"圣经的副本"的书籍应该有一些基本的共同点(内容,即使是不同语言的,有不同的可接受的翻译,脚注的水平和其他注释)。但是,这些书在许多方面是完全可以区别的,这些方面被认为是不基本的——装订的种类、装订的颜色、印刷中使用的字体、插图(如果有的话)、宽的可写页边距、内置书签的数量和种类等等。好的。

一本典型的圣经印刷品很有可能是精装的——毕竟,这是一本通常要反复阅读的书,在几个地方加上书签,翻阅给定的章节和经文指针等,一本精装的书在这种使用下可以使一本给定的书保存更长。然而,这些都是平凡的(实际的)问题,不能用来确定一个给定的实际书籍对象是否是圣经的副本:平装本印刷是完全可能的!好的。

同样,在定义一类语言实现的意义上,python是"一种语言",在某些基本方面(语法,大多数语义,除了那些明确允许它们不同的部分之外)必须都是相似的,但在几乎每一个"实现"细节(包括它们的处理方式)上完全允许不同。有了源文件,它们是将源文件编译成一些较低级别的表单(如果是,那么是哪个表单——以及它们是将这些编译的表单保存到磁盘还是其他地方),它们是如何执行这些表单的,等等。好的。

经典实现cpython通常简称为"python",但它只是几种生产质量实现中的一种,与Microsoft的Ironpython(编译为clr代码,即".net")、jython(编译为jvm代码)、pypyy(用python本身编写,并能编译成各种类型的后端"表单包括"just-in-time"生成的机器语言)。它们都是python(="python语言的实现"),就像许多表面上不同的book对象都可以是bible(="圣经的副本")。好的。

如果您对cpython特别感兴趣:它将源文件编译成特定于python的低级形式(称为"字节码"),在需要时(当没有与源文件对应的字节码文件,或者字节码文件比源文件旧或由不同的python版本编译时),通常会保存字节码。将文件编码到磁盘(以避免将来重新编译它们)。otoh ironpython通常编译为clr代码(根据情况,是否保存到磁盘)和jython到jvm代码(是否保存到磁盘——如果确实保存了它们,它将使用.class扩展名)。好的。

这些较低级别的表单随后由适当的"虚拟机"执行,也称为"解释器"——适当的CPython VM、.NET运行时、Java VM(AKJVM)。好的。

因此,在这个意义上(典型的实现方式是什么),Python是一种"解释的语言",当且仅当C语言和Java是:它们都有一个典型的生成字节码的实现策略,然后通过VM/解释器执行它。好的。

更可能的是,重点是如何"沉重",缓慢,和高仪式的编制过程。cpython被设计为尽可能快地编译,尽可能轻量地编译,尽可能少的繁文缛节——编译器很少进行错误检查和优化,因此它可以在少量的内存中快速运行,这反过来又允许它在需要时自动透明地运行,而用户甚至不需要大部分时间都在编辑。Java和C通常在编译过程中接受更多的工作(因此不执行自动编译),以便更彻底地检查错误并执行更多的优化。这是一个灰色尺度的连续统一体,而不是一个黑白的情况,在某个给定的水平上设置一个阈值,并说只有高于这个水平,你才称之为"编译"!-)好的。好啊。


它们包含字节代码,这就是Python解释器编译源代码的目的。然后,该代码由Python的虚拟机执行。

python的文档解释了如下定义:

Python is an interpreted language, as
opposed to a compiled one, though the
distinction can be blurry because of
the presence of the bytecode compiler.
This means that source files can be
run directly without explicitly
creating an executable which is then
run.


没有解释的语言。无论使用的是解释器还是编译器,都只是实现的一个特性,与语言完全无关。

每种语言都可以由解释器或编译器实现。绝大多数语言至少每种类型都有一个实现。(例如,有C和C++的解释器,并且有JavaScript、PHP、Perl、Python和Ruby的编译器)。此外,大多数现代语言实现实际上同时兼有解释器和编译器(甚至多个编译器)。

语言只是一套抽象的数学规则。解释器是一种语言的几种具体实现策略之一。这两个概念生活在完全不同的抽象层次上。如果英语是一种类型化语言,"解释性语言"一词将是一种类型错误。语句"python is an explocated language"不仅是错误的(因为如果是错误的话,如果是错误的话,就意味着语句甚至是有意义的),而且是简单的也没有意义,因为语言永远不能被定义为"explocated"。

特别是,如果您查看当前现有的Python实现,这些是它们正在使用的实现策略:

  • Ironpython:编译为DLR树,然后由DLR编译为CIL字节码。CIL字节码会发生什么情况取决于您运行的CLI,但是Microsoft.NET、GNU Portable.NET和Novell Mono最终会将其编译为本机代码。
  • Jython:解释python源代码,直到它识别出热代码路径,然后编译成jvml字节码。JVML字节码会发生什么情况取决于您运行的是哪个JVM。Maxine将直接将其编译为未优化的本机代码,直到它识别出热代码路径,然后重新编译为优化的本机代码。Hotspot将首先解释JVML字节码,然后最终将热代码路径编译为优化的机器代码。
  • pypy:编译为pypy字节码,然后由pypy vm解释,直到它识别出热代码路径,然后根据运行的平台将其编译为本机代码、jvml字节码或cil字节码。
  • cpython:编译成cpython字节码,然后进行解释。
  • Stacklesspython:编译成cpython字节码,然后进行解释。
  • unladen swallow:编译到cpython字节码,然后对其进行解释,直到它识别出热代码路径,然后编译到llvm ir,llvm编译器随后编译到本机代码。

您可能会注意到该列表中的每个实现(加上一些我没有提到的实现,比如Tinypy、Shedskin或Psyco)都有一个编译器。事实上,据我所知,目前没有纯粹解释的python实现,也没有计划这样的实现,也没有这样的实现。

术语"解释性语言"不仅没有意义,即使你将其解释为"具有解释性实现的语言",它显然是不正确的。不管是谁告诉你的,显然不知道他在说什么。

特别是,您看到的.pyc文件是由cpython、stackless python或unladen swallow生成的缓存字节码文件。


这些是在导入.py文件时由python解释器创建的,它们包含导入的模块/程序的"编译字节码",其思想是如果.pyc比相应的文件更新,则可以在后续的import上跳过从源代码到字节码的"转换"(只需执行一次)。nding .py文件,因此启动速度有点快。但它仍然被解释。


为了加快加载模块的速度,python将模块的编译内容缓存到.pyc中。

cpython将其源代码编译为"字节代码",出于性能原因,每当源文件发生更改时,它都会将此字节代码缓存到文件系统上。这使得加载Python模块更快,因为编译阶段可以绕过。当源文件是foo.py时,cpython将字节代码缓存在源文件旁边的foo.pyc文件中。

在python3中,python的导入机制被扩展为在每个python包目录内的单个目录中编写和搜索字节代码缓存文件。这个目录将被称为uu pycache_uuuu。

下面是一个描述模块加载方式的流程图:

enter image description here

更多信息:

参考文献:PEP3147参考:"编译"python文件


这是给初学者的,

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

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

例如,如果有一个脚本文件abc.py导入另一个模块xyz.py,则在运行abc.py时,会在导入xyz之后创建xyz.pyc,但不会在不导入abc.py之后创建abc.pyc文件。

如果需要为未导入的模块创建.pyc文件,可以使用py_compilecompileall模块。

py_compile模块可以手动编译任何模块。一种方法是在该模块中交互使用py_compile.compile功能:

1
2
>>> import py_compile
>>> py_compile.compile('abc.py')

这将把.pyc写入与abc.py相同的位置(您可以用可选参数cfile覆盖它)。

还可以使用compileAll模块自动编译目录中的所有文件。

1
python -m compileall

如果省略目录名(本例中的当前目录),则模块编译sys.path上找到的所有内容。


python(至少是它最常见的实现)遵循一种模式,先将原始源代码编译为字节代码,然后在虚拟机上解释字节代码。这意味着(同样,最常见的实现)既不是纯解释器,也不是纯编译器。

然而,另一方面,编译过程基本上是隐藏的,.pyc文件基本上被视为一个缓存;它们加快了速度,但您通常不必知道它们。它会根据文件时间/日期戳在必要时自动失效并重新加载它们(重新编译源代码)。

我唯一一次看到这方面的问题是,编译后的字节码文件不知何故会在将来得到一个时间戳,这意味着它看起来总是比源文件新。由于源文件看起来较新,所以从未重新编译过,因此无论您做了什么更改,都会忽略它们…


python的*.py文件只是一个文本文件,您可以在其中编写一些代码行。尝试使用"python filename.py"执行此文件时

此命令调用python虚拟机。python虚拟机有两个组件:"编译器"和"解释器"。解释器无法直接读取*.py文件中的文本,因此该文本首先被转换为针对pvm的字节代码(不是硬件,而是pvm)。pvm执行这个字节码。*.pyc文件也是作为运行它的一部分生成的,它对shell中的文件或其他文件执行导入操作。

如果已经生成了这个*.pyc文件,那么下次运行/执行您的*.py文件时,系统将直接加载您的*.pyc文件,而不需要任何编译(这将为您节省一些处理器的机器周期)。

一旦生成了*.pyc文件,就不需要*.py文件,除非您对其进行编辑。


python代码经历了两个阶段。第一步将代码编译成.pyc文件,它实际上是一个字节码。然后使用cpython解释器解释这个.pyc文件(字节码)。请参阅此链接。这里用简单的术语解释了代码编译和执行的过程。