首页 文章

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

提问于
浏览
862

我已经理解Python是一种解释型语言...但是,当我查看我的Python源代码时,我看到 .pyc 文件,Windows将其标识为"Compiled Python Files" . 这些来自哪里?

9 回答

  • 20

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

  • 57

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

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

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

  • 558

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

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

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

    特别是,如果您查看当前现有的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生成的缓存字节码文件 .

  • 11

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

  • 7

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

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

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

    这是一个描述如何加载模块的流程图:

    enter image description here

    欲获得更多信息:

    参考:PEP3147
    参考:“Compiled” Python files

  • 136

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

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

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

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

    类似地,Python在定义一个语言类的意义上是"a language",它必须在某些基本方面都是相似的(语法,大多数语义,除了它们给出的那些部分,无论它们是否将源代码编译为某些较低级别的形式) (如果是这样,哪种形式 - 以及它们是否将这些已编译的表格保存到磁盘或其他地方),它们如何执行所述表格,等等 .

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

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

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

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

    更有可能的焦点是编译过程的“重”,慢和高仪式 . CPython旨在尽可能快地编译,尽可能轻量级,尽可能少的仪式 - 编译器执行非常少的错误检查和优化,因此它可以快速运行并且在少量内存中运行,这样就可以了可以在需要时自动且透明地运行,而无需用户甚至需要知道正在进行编译,大多数情况下 . Java和C#通常在编译期间接受更多工作(因此不执行自动编译),以便更彻底地检查错误并执行更多优化 . 它是灰度级的连续体,而不是黑色或白色的情况,并且将阈值设置在某个给定的水平并且仅在该级别之上称为它是完全随意的“汇编”!-)

  • 27

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

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

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

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

  • 26

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

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

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

  • 836

    THIS IS FOR BEGINNERS,

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

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

    例如,如果您有一个脚本文件 abc.py 导入另一个模块 xyz.py ,当您运行 abc.py 时,将自导入xyz以来创建 xyz.pyc ,但由于未导入abc.py,因此没有abc.pyc文件为 created .

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

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

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

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

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

    python -m compileall
    

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

相关问题