如果解释Python,什么是.pyc文件?

fro*_*die 1017 python compiled interpreted-language pyc

我已经理解Python是一种解释型语言......然而,当我查看我的Python源代码时,我看到了.pycWindows,它们识别为"编译的Python文件".这些来自哪里?

Ale*_*lli 962

我已经理解Python是一种解释语言......

这种流行的模因是不正确的,或者更确切地说,是基于对(自然)语言水平的误解而构建的:类似的错误就是说"圣经是精装书".让我解释一下比喻......

"圣经"是"一本书",意思是成为一(实际的,物理对象)书籍; 被确定为"圣经副本"的书应该具有一些基本的共同点(内容,即使是那些可以使用不同语言,具有不同的可接受翻译,脚注和其他注释的水平) - 然而,这些书是完全允许在被认为是基本的无数方面有所区别- 绑定类型,绑定颜色,打印中使用的字体,插图(如果有),宽可写边距与否,内置书签的数量和种类, 等等等等.

很有可能的是,圣经的典型印刷确实是精装书 - 毕竟,它是一本通常意味着一遍又一遍地阅读的书,在几个地方加书签,翻阅寻找给定的章节和诗句指针等等,一个好的精装书绑定可以使给定的副本在这种使用下持续更长时间.然而,这些是平凡的(实际的)问题,不能用于确定给定的实际书籍对象是否是圣经的副本:平装印刷是完全可能的!

类似地,Python是定义一类语言实现的 "语言",它必须在某些基本方面都是相似的(语法,大多数语义,除了那些明确允许它们不同的那些部分)但完全允许几乎每个"实现"细节都有所不同 - 包括他们如何处理他们给出的源文件,他们是否将源代码编译成某种较低级别的形式(如果是,那么哪种形式 - 以及他们是否保存这些形式编译表格,磁盘或其他地方),他们如何执行所述表格,等等.

经典实现CPython通常简称为"Python" - 但它只是几个生产质量实现中的一个,与Microsoft的IronPython(编译为CLR代码,即".NET")并列,Jython (编译为JVM代码),PyPy(用Python本身编写,可以编译成各种各样的"后端"形式,包括"即时"生成的机器语言).它们都是Python(=="Python语言的实现"),就像许多表面上不同的书籍对象都可以是圣经(=="圣经的副本").

如果你对CPython特别感兴趣:它将源文件编译成特定于Python的低级表单(称为"字节码"),在需要时自动执行(当没有与源文件对应的字节码文件时,或者字节码文件比源文件旧,或由不同的Python版本编译),通常将字节码文件保存到磁盘(以避免将来重新编译它们).OTOH IronPython通常会编译为CLR代码(将它们保存到磁盘或不依赖)和Jython保存到JVM代码(将它们保存到磁盘或不保存 - .class如果保存它们将使用扩展名).

然后,这些较低级别的表单由适当的"虚拟机"(也称为"解释器")执行 - CPython VM,.Net运行时,Java VM(也称为JVM).

因此,从这个意义上来说(典型的实现是做什么的),当且仅当C#和Java是:所有这些都具有首先生成字节码的典型实现策略,然后通过VM /解释器执行它时,Python是一种"解释语言" .

更有可能的焦点是编译过程的"重",慢和高仪式.CPython旨在尽可能快地编译,尽可能轻量级,尽可能少的仪式 - 编译器执行非常少的错误检查和优化,因此它可以快速运行并在少量内存中运行,这样就可以了可以在需要时自动且透明地运行,而无需用户甚至需要知道正在进行编译,大多数情况下.Java和C#通常在编译期间接受更多工作(因此不执行自动编译),以便更彻底地检查错误并执行更多优化.它是灰度的连续体,而不是黑色或白色的情况,

  • 非常好的答案.有时候有点形而上和平淡无奇,但它提醒我,看似微不足道的问题可能有相当深刻的答案.即使它没有向我展示天空中的新星,它肯定让我想起了他们的一些名字;-) (148认同)
  • 我读了这个答案,然后我滚动回到upvote只是为了意识到这个已经被我赞成了.这个答案有多好! (32认同)
  • @Elazar,实际上,Python 的其他实现,例如 PyPy,不急于编译,设法进行缺乏静态类型所需的更彻底的分析,并生成对机器代码的即时编译(从而加速多次启动长时间运行的程序)。 (3认同)
  • 当第一个例子是圣经并且答案有666票时,这不是很好吗? (3认同)
  • 很好的答案,但我认为最后一段可以更好地强调 Python 通常被认为是“解释型”语言的主要原因:它是关于*用户(开发人员)体验* - 没有单独的构建步骤。您执行源代码文件(使用 Python 可执行文件作为解释器),并立即让一个进程执行源代码所说的操作。这是“默认”行为——Python 实现的正常甚至“标准”行为。你确实用“仪式”含糊地表达了这一点,但只是在谈论“工作”方面时含糊其辞。 (3认同)
  • 美丽的答案.只是对最后一段的一个小修正:**Python**旨在尽可能快地编译(等等).这次它确实是语言,缺乏静态类型系统和东西.当人们谈论"解释"语言时,它们通常意味着"动态"语言. (2认同)

unw*_*ind 632

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

Python的文档解释了这样的定义:

Python是一种解释型语言,与已编译的语言相反,尽管由于字节码编译器的存在,区别可能很模糊.这意味着可以直接运行源文件,而无需显式创建随后运行的可执行文件.

  • @froadie:语言不是"解释"或"编译"的.特定实现*可以是解释器或编译器(或混合或JIT编译器). (189认同)
  • "编译"的一个测试:是否编译为实际的机器指令?Python字节码不是机器指令,也不是Java'JVM'指令,因此这些语言都不是由该定义编译的.但两者都"编译"为一个中间的"抽象机器"代码,并且两者都比运行程序更快或更快,直接解释源代码(这是老派BASIC所做的). (29认同)
  • 为了迂腐,"编译"意味着"翻译".然后将Python编译为字节码.AFAIK,只有Bash被*解释,所有其他流行的"解释"语言都编译成字节码. (19认同)
  • 实际上,它们是*机器指令,而不是主机物理CPU的*本机*机器指令.那么为什么我们称它为VM?就像世界语汇编语言一样.如今我们甚至拥有虚构(但仍然模仿)CPU的本机代码(Mojang努力让小孩感兴趣).Rexx是(或可能)真正解释,并解释BAT和CMD(和DCL). (13认同)
  • 有意思,谢谢.那么Python被认为是一种纯粹的解释语言吗? (10认同)
  • @Danijel:导入模块时,python会自动检测.py文件是否已被修改,并根据需要自动重新编译新的.pyc/.pyo.在大多数情况下,您永远不必担心管理.pyc/.pyo文件. (3认同)
  • @Mike是的,尽管`*.pyc`文件并不总是写入磁盘。 (3认同)
  • @GuyAvraham:通常你想在你的 `.gitignore` 中添加一行,以防止它跟踪 `*.py[co]`(以及你已经提交的 `git rm`,这样它们就不会出现在其他人克隆它)。在 `git` 中跟踪它们的成本远远超过收益。从源代码编译到字节码,包括所有磁盘 I/O,的成本通常在个位数毫秒范围内(测试在 Python 的内置 `_collections_abc.py` 上调用 `py_compile.compile` 输出到垃圾文件一个 26 KB 的文件大约需要 8 毫秒)。克隆后*一次*为几十个文件支付这笔费用是微不足道的。 (2认同)
  • @greggo Nuitka 可以通过机器相关的 C++ 将整个 Python 编译为机器代码,而 Cython 也可以类似地将 Python 编译为可移植的 C 代码。 (2认同)

Jör*_*tag 160

没有解释语言这样的东西.是否使用解释器或编译器纯粹是实现的特性,并且绝对没有任何与该语言有关的内容.

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

语言只是一组抽象的数学规则.解释器是语言的几种具体实现策略之一.这两个人生活在完全不同的抽象层次上.如果英语是键入语言,则术语"解释语言"将是类型错误.语句"Python是一种解释型语言"不仅仅是错误的(因为错误意味着该语句甚至有意义,即使它是错误的),它只是简单的没有意义,因为语言永远不能被定义为"解释."

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

  • IronPython:编译为DLR树,然后DLR编译为CIL字节码.CIL字节码会发生什么取决于您运行的CLI VES,但Microsoft .NET,GNU Portable.NET和Novell Mono最终会将其编译为本机机器代码.
  • Jython:解释Python源代码,直到它识别出热代码路径,然后编译成JVML字节码.JVML字节码会发生什么取决于您运行的JVM.Maxine将直接将其编译为未优化的本机代码,直到它识别出热代码路径,然后将其重新编译为优化的本机代码.HotSpot将首先解释JVML字节码,然后最终将热代码路径编译为优化的机器代码.
  • PyPy:编译为PyPy字节码,然后由PyPy VM解释,直到它识别出热代码路径,然后根据您运行的平台编译成本机代码,JVML字节码或CIL字节码.
  • CPython:编译成CPython字节码,然后解释它.
  • Stackless Python:编译成CPython字节码,然后解释它.
  • Unladen Swallow:编译成CPython字节码,然后解释它直到识别出热代码路径然后编译成LLVM IR,然后LLVM编译器将编译为本机机器代码.

您可能会注意到该列表中的每个实现(以及我未提及的其他一些实现,如tinypy,Shedskin或Psyco)都有一个编译器.事实上,据我所知,目前还没有纯粹解释的Python实现,没有计划这样的实现,并且从未有过这样的实现.

"解释语言"这个术语不仅没有意义,即使你将其解释为"具有解释性实现的语言",显然也不是这样.无论谁告诉你,显然不知道他在说什么.

特别是,.pyc您看到的文件是由CPython,Stackless Python或Unladen Swallow生成的缓存字节码文件.

  • MSBASIC等老派基础没有中间形式.程序直接从源代码形式解释(或在源代码附近,一种形式,其中关键字由1字节标记表示,行#由2字节二进制整数表示,但其余部分只是ASCII).所以实际上'goto'会占用不同的时间,具体取决于搜索匹配目的地所需搜索的源行数.像*b-2*cos(x)这样的表达式在每次执行时都被有效地重新解析. (5认同)
  • @greggo:如果你想进入更老的学校,BASIC的原始*版本是本机代码编译器.这应该证明"编译"或"解释"语言的概念是多么荒谬. (4认同)
  • @personal_cloud:我不太理解你的评论。是的,当然,我知道 Cython,但这与什么有什么关系?它不是 Python 的实现,它是一种完全不同的语言。另外,要找到一个 JavaScript 示例真的不难,事实上,*所有*当前现有的主流 JavaScript 实现都有编译器。最后,Jython 是 Python 的一种实现,就像 Python 的任何其他实现一样。它是 Java 平台上一种语言的实现,就像 Java 平台上的任何其他语言实现一样。 (2认同)

Tim*_*ker 60

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

  • 真正.除了许多核心Python库都是用C语言编写的.因此python运行的部分解释,部分运行在C中.您可以对自己的性能敏感位代码执行相同操作. (10认同)

hxy*_*yhi 39

为了加速加载模块,Python在.pyc中缓存模块的编译内容.

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

在python3中,Python的导入机制被扩展为在每个Python包目录内的单个目录中编写和搜索字节代码缓存文件.该目录将被称为__pycache__.

以下是描述如何加载模块的流程图:

在此输入图像描述

欲获得更多信息:

参考:PEP3147
参考:"编译"的Python文件

  • Python 检查 .pyc 文件的内部时间戳是否不早于相应的 .py 文件。@KaushalPrajapati (2认同)

MAX*_*MAX 37

这是为了初学者,

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

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

举例来说,如果你有一个脚本文件abc.py一个导入另一个模块xyz.py,当你运行abc.py,xyz.pyc将被创建,因为XYZ是进口的,但没有abc.pyc文件将被创建以来的ABC. py未被导入.

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

py_compile模块可以手动编译任何模块.一种方法是以py_compile.compile交互方式使用该模块中的函数:

>>> import py_compile
>>> py_compile.compile('abc.py')
Run Code Online (Sandbox Code Playgroud)

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

您还可以使用compileall模块自动编译目录中的所有文件.

python -m compileall
Run Code Online (Sandbox Code Playgroud)

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

  • 获取abc.py的编译有什么好处? (6认同)

Jer*_*fin 20

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

然而,另一方面,编译过程大多是隐藏的 - .pyc文件基本上被视为缓存; 他们加快了速度,但你通常根本不需要了解它们.它会根据文件时间/日期戳自动使其无效并重新加载(重新编译源代码).

大约在我看到这个问题的唯一一次是当编译的字节码文件以某种方式在未来很好地得到时间戳时,这意味着它总是看起来比源文件更新.由于它看起来更新,源文件从未被重新编译,所以无论你做了什么改变,它们都会被忽略......

  • 哇.这肯定是一个痛苦,弄清楚.pyc文件有一个比.py文件更新的时间戳. (2认同)

小智 12

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

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

如果已生成此*.pyc文件,则每次下次运行/执行*.py文件时,系统都会直接加载您的*.pyc文件,这不需要任何编译(这样可以节省一些处理器的机器周期).

生成*.pyc文件后,除非您编辑它,否则不需要*.py文件.


mur*_*tio 8

tldr; 它是源代码的转换代码,python VM 将其解释为执行。

自下而上的理解:任何程序的最后阶段都是在硬件/机器上运行/执行程序的指令。所以这里是执行前的阶段:

  1. 在 CPU 上执行/运行

  2. 将字节转换为机器码

    • 机器码是转换的最后阶段。

    • 要在 CPU 上执行的指令以机器代码给出。机器码可以直接由CPU执行

  3. 字节码转换为机器码。

    • 字节码是一个中间阶段。为了效率可以跳过它,但会牺牲可移植性
  4. 源代码转换为字节

    • 源代码是人类可读的代码。这是在Pycharm等IDE(代码编辑器)上工作时使用的。

现在的实际情节。在执行这些阶段中的任何一个时,有两种方法:一次转换 [或执行] 代码(又名compile)和逐行转换 [或执行] 代码(又名interpret)。

  • 例如,我们可以将源代码编译为 bytcoe,将字节码编译为机器码,将机器码解释为执行。

  • 一些语言的实现跳过了第三阶段以提高效率,即将源代码编译成机器代码,然后解释机器代码以执行。

  • 一些实现会跳过所有中间步骤,直接解释源代码以供执行。

  • 现代语言通常涉及编译和解释

  • 以JAVA为例,将源代码编译为字节码[即JAVA源代码的存储方式,作为字节码],将字节码编译为机器码[使用JVM],并解释机器码以供执行。[因此对于不同的操作系统,JVM 的实现方式不同,但相同的 JAVA 源代码可以在安装了 JVM 的不同操作系统上执行。]

  • 以Python为例,将源代码编译为bytcode[通常是.py源代码附带的.pyc文件],将bytocde编译为机器码[由虚拟机如PVM完成,结果是一个可执行文件],解释机器用于执行的代码/可执行文件。

  • 我们什么时候可以说一种语言是解释型的或编译型的?

    • 答案是通过研究执行中使用的方法。如果它一次执行所有机器代码(== 编译),那么它就是一种编译语言。另一方面,如果它逐行执行机器代码(==解释),那么它就是一种解释型语言。
  • 因此,JAVA 和 Python 是解释型语言。

  • 一个混乱可能会因为第三阶段,这是转换bytcode成机器代码的发生。这通常是使用称为虚拟机的软件完成的。之所以会出现这种混淆,是因为虚拟机的行为就像一台机器,但实际上并非如此!虚拟机是为了可移植性而引入的,在任何真实机器上拥有虚拟机将允许我们执行相同的源代码。大多数VM使用的方法[即第三阶段]是编译,因此有些人会说它是一种编译语言。对于VM的重要性,我们常说这类语言既是编译型的,也是解释型的


Tan*_*ngo 7

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