我已经了解 Python 是一种解释性语言......
但是,当我查看我的Python源代码时.pyc
,我看到了 Windows 将其标识为“编译的 Python 文件”的文件。
这些从哪里来?
我已经了解 Python 是一种解释性语言......
但是,当我查看我的Python源代码时.pyc
,我看到了 Windows 将其标识为“编译的 Python 文件”的文件。
这些从哪里来?
我已经明白 Python 是一种解释性语言......
这个流行的模因是不正确的,或者更确切地说,是建立在对(自然)语言水平的误解之上:类似的错误是说“圣经是一本精装书”。让我解释一下这个比喻......
“圣经”是“一本书”,是指一类(实际的、有形的物体)书籍;被认定为“圣经副本”的书籍应该有一些基本的共同点(内容,尽管即使是不同的语言,有不同的可接受的翻译,脚注和其他注释的水平)——然而,这些书是完全可以在不被认为是基本的无数方面有所不同——装订类型、装订颜色、印刷中使用的字体、插图(如果有)、可写页边距是否宽、内置书签的数量和种类, 等等等等。
一本典型的圣经印刷版很可能确实是精装装订的——毕竟,这本书通常意味着要一遍又一遍地阅读,在几个地方加了书签,翻阅寻找给定的章节和经文指针,等等,等等,一个好的精装装订可以使给定的副本在这种使用下持续更长时间。然而,这些都是平凡(实际)的问题,不能用来确定给定的实际书籍对象是否是圣经的副本:平装印刷是完全可能的!
类似地,Python 在定义一类语言实现的意义上是“一种语言”,这些实现在某些基本方面都必须相似(语法,大多数语义,除了那些明确允许它们不同的部分)但完全允许几乎在每个“实现”细节上都有所不同——包括他们如何处理给定的源文件,他们是否将源代码编译为一些较低级别的形式(如果是的话,是哪种形式——以及他们是否保存这些形式)已编译的表格,磁盘或其他地方),它们如何执行所述表格,等等。
经典的实现,CPython,通常简称为“Python”——但它只是几个生产质量实现之一,与微软的 IronPython(编译为 CLR 代码,即“.NET”)、Jython (编译成 JVM 代码),PyPy(它本身是用 Python 编写的,可以编译成各种各样的“后端”形式,包括“即时”生成的机器语言)。它们都是 Python(=="Python 语言的实现"),就像许多表面上不同的书籍对象都可以是圣经(=="圣经副本")。
如果您特别对 CPython 感兴趣:它将源文件编译为特定于 Python 的低级形式(称为“字节码”),在需要时自动编译(当没有与源文件对应的字节码文件时,或者字节码文件比源代码旧或由不同的 Python 版本编译),通常将字节码文件保存到磁盘(以避免将来重新编译它们)。OTOH IronPython 通常会编译为 CLR 代码(是否将它们保存到磁盘,具体取决于),并将 Jython 编译为 JVM 代码(是否将它们保存到磁盘——.class
如果保存它们,它将使用扩展名)。
然后,这些较低级别的形式由适当的“虚拟机”(也称为“解释器”)执行——CPython VM、.Net 运行时、Java VM(也称为 JVM),视情况而定。
因此,从这个意义上说(典型实现是做什么的),Python 是一种“解释型语言”当且仅当 C# 和 Java 是:它们都有一个典型的实现策略,首先生成字节码,然后通过 VM/解释器执行它.
更有可能的重点是编译过程的“繁重”、缓慢和隆重。CPython 被设计为尽可能快地编译,尽可能轻量级,尽可能少的仪式——编译器几乎不做错误检查和优化,因此它可以在少量内存中快速运行,这反过来又让它在需要时自动和透明地运行,大多数时候用户甚至不需要知道正在进行编译。Java 和 C# 通常在编译期间接受更多工作(因此不执行自动编译),以便更彻底地检查错误并执行更多优化。这是灰阶的连续统一体,而不是非黑即白的情况,
它们包含字节码,这是 Python 解释器将源代码编译成的内容。然后,此代码由 Python 的虚拟机执行。
Python 的文档解释了这样的定义:
Python 是一种解释语言,而不是编译语言,尽管由于字节码编译器的存在,区别可能会很模糊。这意味着可以直接运行源文件,而无需显式创建随后运行的可执行文件。
没有解释性语言这样的东西。是否使用解释器或编译器纯粹是实现的特征,与语言绝对无关。
每种语言都可以由解释器或编译器实现。绝大多数语言对每种类型都至少有一个实现。(例如,C 和 C++ 有解释器,JavaScript、PHP、Perl、Python 和 Ruby 有编译器。)此外,大多数现代语言实现实际上结合了解释器和编译器(甚至多个编译器)。
语言只是一组抽象的数学规则。解释器是一种语言的几种具体实现策略之一。这两者生活在完全不同的抽象级别上。如果英语是一种类型化语言,那么术语“解释语言”将是类型错误。“Python 是一种解释性语言”这句话不仅是错误的(因为错误意味着该语句甚至是有意义的,即使它是错误的),它只是简单地没有意义,因为一种语言永远不能被定义为“翻译。”
特别是,如果您查看当前现有的 Python 实现,这些是它们正在使用的实现策略:
您可能会注意到该列表中的每一个实现(以及我没有提到的其他一些实现,例如 tinypy、Shedskin 或 Psyco)都有一个编译器。事实上,据我所知,目前还没有纯粹解释的 Python 实现,没有计划过这样的实现,也从来没有这样的实现。
不仅“解释性语言”这个词没有意义,即使您将其解释为“具有解释性实现的语言”,这显然也不正确。谁告诉你的,显然不知道他在说什么。
特别是,.pyc
您看到的文件是由 CPython、Stackless Python 或 Unladen Swallow 生成的缓存字节码文件。
这些是在导入文件时由 Python 解释器创建的.py
,它们包含导入模块/程序的“编译字节码”,其想法是从源代码“翻译”到字节码(只需要完成一次)如果 s比相应的文件更新,则可以在后续import
s上跳过,从而加快启动速度。但它仍然被解释。.pyc
.py
为了加快加载模块,Python 将模块的编译内容缓存在 .pyc 中。
CPython 将其源代码编译为“字节码”,出于性能原因,每当源文件发生更改时,它都会将此字节码缓存在文件系统中。这使得 Python 模块的加载速度更快,因为可以绕过编译阶段。当您的源文件是 foo.py 时,CPython 将字节码缓存在源文件旁边的 foo.pyc 文件中。
在 python3 中,Python 的导入机制被扩展为在每个 Python 包目录内的单个目录中写入和搜索字节码缓存文件。这个目录将被称为 __pycache__ 。
这是描述如何加载模块的流程图:
了解更多信息:
参考:PEP3147
参考:“已编译”的 Python 文件
这是给初学者的,
Python 会在运行之前自动将您的脚本编译为已编译的代码,即所谓的字节码。
运行脚本不被视为导入,并且不会创建 .pyc。
例如,如果您有一个脚本文件abc.py导入另一个模块xyz.py,当您运行abc.py时,将创建xyz.pyc ,因为 xyz 被导入,但不会创建abc.pyc 文件自 abc 。 py 没有被导入。
如果需要为未导入的模块创建 .pyc 文件,可以使用py_compile
andcompileall
模块。
该py_compile
模块可以手动编译任何模块。一种方法是py_compile.compile
交互地使用该模块中的函数:
>>> import py_compile
>>> py_compile.compile('abc.py')
这会将 .pyc 写入与 abc.py 相同的位置(您可以使用可选参数覆盖它cfile
)。
您还可以使用 compileall 模块自动编译一个或多个目录中的所有文件。
python -m compileall
如果省略了目录名(本例中为当前目录),模块将编译在sys.path
Python(至少是它最常见的实现)遵循将原始源代码编译为字节码,然后在虚拟机上解释字节码的模式。这意味着(同样,最常见的实现)既不是纯解释器也不是纯编译器。
然而,另一方面,编译过程大部分是隐藏的—— .pyc 文件基本上被视为缓存;它们加快了速度,但您通常根本不需要注意它们。它会根据文件时间/日期戳在必要时自动使它们无效并重新加载(重新编译源代码)。
大约我唯一一次看到这个问题是当编译的字节码文件以某种方式获得未来的时间戳时,这意味着它总是看起来比源文件更新。由于它看起来更新,源文件从未重新编译,所以无论你做了什么更改,它们都被忽略了......
Python 的 *.py 文件只是一个文本文件,您可以在其中编写一些代码行。当您尝试使用“python filename.py”执行此文件时
此命令调用 Python 虚拟机。Python 虚拟机有 2 个组件:“编译器”和“解释器”。解释器无法直接读取 *.py 文件中的文本,因此该文本首先被转换为针对 PVM (不是硬件,而是 PVM)的字节码。PVM 执行这个字节码。*.pyc 文件也会生成,作为运行它的一部分,它对 shell 或其他文件中的文件执行导入操作。
如果这个 *.pyc 文件已经生成,那么每次你运行/执行你的 *.py 文件时,系统会直接加载你的 *.pyc 文件,它不需要任何编译(这将为你节省一些处理器的机器周期)。
一旦生成 *.pyc 文件,就不需要 *.py 文件,除非你编辑它。
tldr; it's a converted code form the source code, which the python VM interprets for execution.
Bottom-up understanding: the final stage of any program is to run/execute the program's instructions on the hardware/machine. So here are the stages preceding execution:
Executing/running on CPU
Converting bytcode to machine code.
Machine code is the final stage of conversion.
Instructions to be executed on CPU are given in machine code. Machine code can be executed directly by CPU.
Converting Bytecode to machine code.
Converting Source code to bytcode.
Now the actual plot. There are two approaches when carrying any of these stages: convert [or execute] a code all at once (aka compile) and convert [or execute] the code line by line (aka interpret).
For example, we could compile a source code to bytcoe, compile bytecode to machine code, interpret machine code for execution.
Some implementations of languages skip stage 3 for efficiency, i.e. compile source code into machine code and then interpret machine code for execution.
Some implementations skip all middle steps and interpret the source code directly for execution.
Modern languages often involve both compiling an interpreting.
JAVA for example, compile source code to bytcode [that is how JAVA source is stored, as a bytcode], compile bytcode to machine code [using JVM], and interpret machine code for execution. [Thus JVM is implemented differently for different OSs, but the same JAVA source code could be executed on different OS that have JVM installed.]
Python for example, compile source code to bytcode [usually found as .pyc files accompanying the .py source codes], compile bytocde to machine code [done by a virtual machine such as PVM and the result is an executable file], interpret the machine code/executable for execution.
When we can say that a language is interpreted or compiled?
Therefore, JAVA and Python are interpreted languages.
A confusion might occur because of the third stage, that's converting bytcode to machine code. Often this is done using a software called a virtual machine. The confusion occurs because a virtual machine acts like a machine, but it's actually not! Virtual machines are introduced for portability, having a VM on any REAL machine will allow us to execute the same source code. The approach used in most VMs [that's the third stage] is compiling, thus some people would say it's a compiled language. For the importance of VMs, we often say that such languages are both compiled and interpreted.
Python 代码经历了 2 个阶段。第一步将代码编译成 .pyc 文件,这实际上是一个字节码。然后使用 CPython 解释器解释这个 .pyc 文件(字节码)。请参考此链接。这里用简单的术语解释了代码编译和执行的过程。
将语言规范与语言实现区分开来很重要:
任何编译器都由两个独立的部分组成:前端和后端。前端接收源代码,对其进行验证并将其转换为中间代码。之后,后端将其转换为机器代码以在物理机或虚拟机中运行。解释器是一个编译器,但在这种情况下,它可以产生一种直接在虚拟机中执行中间代码的方式。要执行python代码,必须将代码转换为中间代码,然后将代码“组装”为可以存储在file.pyc中的字节码,因此每次运行程序时都无需编译模块. 您可以使用以下命令查看此组装好的 Python 代码:
from dis import dis
def a(): pass
dis(a)
任何人都可以用 Python 语言为静态二进制构建编译器,就像可以为 C 语言构建解释器一样。有一些工具 ( lex/yacc ) 可以简化和自动化构建编译器的过程。
机器不理解英语或任何其他语言,它们只理解字节码,它们必须被编译(例如,C/C++、Java)或解释(例如,Ruby、Python),.pyc 是字节码。 https://www.geeksforgeeks.org/difference-between-compiled-and-interpreted-language/ 这里快速阅读编译语言与解释语言的区别,TLDR 是解释语言,不需要你全部编译运行时之前的代码,因此大多数时候它们对键入等并不严格。