如果 Python 是解释型的,那么 .pyc 文件是什么?
- 2024-12-20 08:37:00
- admin 原创
- 74
问题描述:
Python 是一种解释型语言。但是为什么我的源目录包含.pyc
Windows 识别为“已编译的 Python 文件”的文件?
解决方案 1:
据我了解,Python 是一种解释型语言......
这个流行的梗是错误的,或者说,它是基于对(自然)语言水平的误解而建立的:类似的错误是说“圣经是一本精装书”。让我来解释一下这个比喻……
“圣经”是“一本书”,因为它是一类(实际的、被识别为)书籍;被识别为“圣经副本”的书籍应该有一些基本的共同点(内容,尽管这些内容可以是不同的语言,有不同的可接受的翻译、脚注和其他注释的级别)——但是,这些书籍在许多不被认为是基本方面的方面完全可以有所不同——装订类型、装订颜色、印刷时使用的字体、插图(如果有)、可写边距是否宽、内置书签的数量和种类,等等。
典型的《圣经》印刷很可能确实是精装的——毕竟,这本书通常需要反复阅读,在多个地方做书签,翻阅以寻找给定的章节和诗句指针,等等,而好的精装装订可以使给定的副本在这种使用情况下保存更长时间。然而,这些都是平凡的(实际的)问题,不能用来判断给定的实际书籍对象是否是《圣经》的副本:平装印刷是完全可能的!
类似地,Python 是“一种语言”,从某种意义上说,它定义了一类语言实现,它们在某些基本方面(语法、大多数语义,除了明确允许不同的部分)必须相似,但几乎在每个“实现”细节上都可以有所不同——包括它们如何处理给定的源文件,它们是否将源编译为某些较低级别的形式(如果是,是哪种形式——以及它们是否将这些编译的形式保存到磁盘或其他地方),它们如何执行所述形式,等等。
经典实现 CPython 通常简称为“Python”——但它只是几种生产质量实现之一,与微软的 IronPython(编译为 CLR 代码,即“.NET”)、Jython(编译为 JVM 代码)、PyPy(用 Python 本身编写,可以编译为各种“后端”形式,包括“即时”生成的机器语言)并列。它们都是 Python(==“Python 语言的实现”),就像许多表面上不同的书籍对象都可以是圣经(==“圣经的副本”)。
如果您特别对 CPython 感兴趣:它会将源文件编译为 Python 特定的低级形式(称为“字节码”),在需要时自动执行此操作(当没有与源文件对应的字节码文件,或者字节码文件比源代码旧或由不同的 Python 版本编译时),通常会将字节码文件保存到磁盘(以避免将来重新编译它们)。另一方面,IronPython 通常会编译为 CLR 代码(是否将它们保存到磁盘,视情况而定),并将 Jython 编译为 JVM 代码(是否将它们保存到磁盘 -.class
如果它保存它们,它将使用扩展)。
然后,这些较低级别的形式由适当的“虚拟机”(也称为“解释器”)执行 - CPython VM、.Net 运行时、Java VM(又名 JVM),视情况而定。
因此,从这个意义上讲(典型的实现做什么),Python 是一种“解释型语言”,当且仅当 C# 和 Java 也是这样:它们都有一个典型的实现策略,即首先生成字节码,然后通过 VM/解释器执行它。
更可能的是,重点在于编译过程有多“繁重”、缓慢和繁琐。CPython 旨在尽可能快地、尽可能轻量地进行编译,尽可能少地进行繁琐的编译 —— 编译器几乎不进行错误检查和优化,因此它可以快速运行,并且占用很少的内存,这反过来又让它可以在需要时自动透明地运行,而用户大多数时候甚至不需要知道正在进行编译。Java 和 C# 通常在编译期间接受更多工作(因此不执行自动编译),以便更彻底地检查错误并执行更多优化。这是一个灰度的连续体,而不是黑色或白色的情况,将阈值设置为某个给定级别并说只有在该级别之上才称之为“编译”是完全任意的!-)
解决方案 2:
它们包含字节码,即 Python 解释器将源代码编译成的字节码。然后,Python 的虚拟机会执行此代码。
Python 的文档对该定义的解释如下:
Python 是一种解释型语言,而不是编译型语言,但由于存在字节码编译器,两者的区别可能比较模糊。这意味着可以直接运行源文件,而无需显式创建可执行文件然后运行。
解决方案 3:
不存在解释型语言。使用解释器还是编译器纯粹是实现的特性,与语言本身毫无关系。
每种语言都可以由解释器或编译器实现。绝大多数语言至少有每种类型的一种实现。(例如,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 编译器将其编译为本机机器代码。
Cython:将 Python 代码编译为可移植的 C 代码,然后使用标准 C 编译器进行编译
Nuitka:将 Python 代码编译为机器相关的 C++ 代码,然后使用标准 C 编译器进行编译
您可能会注意到,该列表中的每个实现(以及一些我没有提到的其他实现,例如 tinypy、Shedskin 或 Psyco)都有一个编译器。事实上,据我所知,目前没有纯粹解释型的 Python 实现,没有计划实现这样的实现,也从未有过这样的实现。
不仅“解释型语言”这个术语毫无意义,即使你将其解释为“具有解释型实现的语言”,也显然不是事实。告诉你这个的人显然不知道自己在说什么。
具体来说,.pyc
您看到的文件是 CPython、Stackless Python 或 Unladen Swallow 生成的缓存字节码文件。
解决方案 4:
这些是由 Python 解释器在.py
导入文件时创建的,它们包含导入模块/程序的“编译后的字节码”,其理念是,import
如果.pyc
比相应.py
文件更新,则可以在后续文件中跳过从源代码到字节码的“翻译”(只需执行一次),从而稍微加快启动速度。但它仍是解释性的。
解决方案 5:
为了加快模块加载速度,Python 将模块的编译内容缓存在 .pyc 中。
CPython 将其源代码编译为“字节码”,出于性能原因,每当源文件发生更改时,它都会将此字节码缓存在文件系统中。这使得 Python 模块的加载速度更快,因为可以绕过编译阶段。当您的源文件是 foo.py 时,CPython 会将字节码缓存在紧邻源代码的 foo.pyc 文件中。
在 python3 中,Python 的导入机制得到扩展,可在每个 Python 包目录内的单个目录中写入和搜索字节码缓存文件。此目录将被称为 pycache 。
以下是描述如何加载模块的流程图:
更多信息:
参考:PEP3147
参考:“编译的” Python 文件
解决方案 6:
这是给初学者的,
在运行脚本之前,Python 会自动将其编译为编译代码,即所谓的字节码。
运行脚本不被视为导入,并且不会创建 .pyc。
例如,如果您有一个脚本文件abc.py导入了另一个模块xyz.py,当您运行abc.py时,由于 xyz 已被导入,因此将创建xyz.pyc ,但由于 abc.py 未被导入,因此不会创建abc.pyc 文件。
如果需要为未导入的模块创建.pyc 文件,可以使用py_compile
和compileall
模块。
模块py_compile
可以手动编译任何模块。一种方法是交互地使用py_compile.compile
该模块中的函数:
>>> import py_compile
>>> py_compile.compile('abc.py')
这会将 .pyc 写入与 abc.py 相同的位置(您可以使用可选参数覆盖它cfile
)。
您还可以使用 compileall 模块自动编译一个或多个目录中的所有文件。
python -m compileall
如果省略目录名(本例中为当前目录),模块将编译在sys.path
解决方案 7:
Python(至少是其最常见的实现)遵循将原始源代码编译为字节码,然后在虚拟机上解释字节码的模式。这意味着(同样,最常见的实现)既不是纯解释器也不是纯编译器。
然而,另一方面,编译过程大部分是隐藏的——.pyc 文件基本上被视为缓存;它们可以加快速度,但您通常根本不需要注意它们。它会根据文件时间/日期戳在必要时自动使它们失效并重新加载(重新编译源代码)。
我唯一一次看到这种问题就是,编译后的字节码文件不知何故获得了一个未来很长的时间戳,这意味着它看起来总是比源文件新。由于它看起来较新,源文件从未被重新编译,所以无论你做了什么更改,它们都会被忽略……
解决方案 8:
Python 的 *.py 文件只是一个文本文件,您可以在其中编写几行代码。当您尝试使用“python filename.py”执行此文件时
此命令调用 Python 虚拟机。Python 虚拟机有两个组件:“编译器”和“解释器”。解释器无法直接读取 .py 文件中的文本,因此首先将该文本转换为针对 PVM (不是硬件而是 PVM)的字节码。PVM 执行此字节码。还会生成 .pyc 文件,作为运行它的一部分,它会在 shell 或其他文件中对文件执行导入操作。
如果这个 .pyc 文件已经生成,那么下次您运行/执行 .py 文件时,系统会直接加载您的 *.pyc 文件,而不需要任何编译(这将节省您的一些处理器的机器周期)。
一旦生成了 .pyc 文件,就不再需要 .py 文件,除非你编辑它。
解决方案 9:
tldr;它是从源代码转换而来的代码,由 python VM 解释并执行。
自下而上的理解:任何程序的最后阶段都是在硬件/机器上运行/执行程序的指令。因此,以下是执行之前的阶段:
在 CPU 上执行/运行
将字节码转换为机器码。
* 机器码是转换的最后阶段。
* CPU 上要执行的**指令**以机器代码的形式给出。机器代码可以由 CPU**直接执行。**
将字节码转换为机器码。
* **字节码是一个中间阶段。为了提高效率**可以跳过它,但是会牺牲**可移植性**。
将源代码转换为字节码。
* 源代码是**人类可读的**代码。这是在使用Pycharm 等**IDE (代码编辑器)时使用的代码。**
现在来看看实际情节。执行任何这些阶段时都有两种方法:一次性转换 [或执行] 代码(又名编译)和逐行转换 [或执行] 代码(又名解释)。
例如,我们可以将源代码编译为字节码,将字节码编译为机器码,解释机器码以供执行。
有些语言的实现为了提高效率会跳过第 3 阶段,即将源代码编译为机器码,然后解释机器码执行。
有些实现跳过所有中间步骤并直接解释源代码以执行。
现代语言通常同时涉及编译和解释。
例如,JAVA 将源代码编译为字节码 [这就是 JAVA 源代码的存储方式,以字节码的形式,将字节码编译为机器码 [使用 JVM],并解释机器码以供执行。[因此,JVM 针对不同的操作系统有不同的实现,但相同的 JAVA 源代码可以在安装了 JVM 的不同操作系统上执行。]
以 Python 为例,将源代码编译为字节码 [通常以.py 源代码附带的.pyc 文件形式存在],将字节码编译为机器码 [由 PVM 等虚拟机完成,结果是可执行文件],解释机器码/可执行文件以供执行。
什么时候我们可以说一种语言是解释型或编译型的?
+ 答案是查看执行时使用的方法。如果它一次性执行机器代码(==编译),那么它就是编译型语言。另一方面,如果它逐行执行机器代码(==解释),那么它就是解释型语言。
因此,JAVA和Python都是解释型语言。
第三阶段可能会引起混淆,即将字节码转换为机器码。这通常使用称为虚拟机的软件来完成。造成混淆的原因是虚拟机的行为像机器一样,但实际上它不是机器!引入虚拟机是为了实现可移植性,在任何真实的机器上安装虚拟机都可以让我们执行相同的源代码。大多数虚拟机(即第三阶段)使用的方法是编译,因此有些人会说它是一种编译型语言。由于虚拟机的重要性,我们经常说这种语言既是编译型又是解释型。
解决方案 10:
Python 代码要经过两个阶段。第一步是将代码编译成 .pyc 文件,这实际上是字节码。然后使用 CPython 解释器解释此 .pyc 文件(字节码)。请参阅此链接。这里以简单的术语解释了代码编译和执行的过程。
解决方案 11:
区分语言规范和语言实现很重要:
语言规范只是一份包含语言形式规范的文档,具有上下文无关语法和语义规则的定义(如指定原始类型和范围动态)。
语言实现只是一个按照语言规范来实现语言使用的程序(编译器)。
任何编译器都由两个独立的部分组成:前端和后端。前端接收源代码,验证它并将其转换为中间代码。之后,后端将其转换为机器代码,以便在物理机或虚拟机中运行。解释器是一种编译器,但在这种情况下,它可以产生一种在虚拟机中直接执行中间代码的方法。要执行 Python 代码,必须将代码转换为中间代码,之后代码将被“汇编”为可以存储在 .pyc 文件中的字节码,因此每次运行程序时都无需编译程序模块。您可以使用以下方法查看此汇编 Python 代码:
from dis import dis
def a(): pass
dis(a)
任何人都可以用 Python 语言构建静态二进制编译器,也可以构建C 语言解释器。有工具 ( lex/yacc ) 可以简化和自动化构建编译器的过程。
解决方案 12:
机器不懂英语或任何其他语言,它们只懂字节码,必须对字节码进行编译(例如 C/C++、Java)或解释(例如 Ruby、Python),.pyc 是字节码的缓存版本。https:
//www.geeksforgeeks.org/difference-between-compiled-and-interpreted-language/
这里简要介绍了编译型语言和解释型语言之间的区别,TLDR 是解释型语言不需要您在运行之前编译所有代码,因此大多数时候它们对类型等并不严格。
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理必备:盘点2024年13款好用的项目管理软件