如果Python是解释型的,那么.pyc文件是什么?
Python是一种解释型语言。那么,为什么我的源代码目录里会有.pyc
文件呢?这些文件在Windows中被识别为“编译的Python文件”?
12 个回答
其实没有所谓的“解释型语言”。使用解释器还是编译器完全是实现的特点,跟语言本身没有任何关系。
每一种语言都可以用解释器或编译器来实现。绝大多数语言都有这两种实现方式。(比如,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虚拟机解释,直到识别出热点代码路径,然后根据你运行的平台将其编译成本地代码、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生成的缓存字节码文件。
我了解到,Python是一种解释型语言……
这个流行的说法其实是不对的,或者说是基于对语言层次的误解。就像说“圣经是一本精装书”一样。让我来解释一下这个比喻……
“圣经”是“一本书”,在这里指的是一类(实际的、物理的被称为)书籍;被称为“圣经副本”的书籍应该有一些基本的共同点(内容,虽然这些内容可以用不同的语言,不同的翻译,注释的多少等来表现)——然而,这些书在许多不被认为是基本的方面是完全可以不同的——比如装订方式、封面的颜色、印刷时使用的字体、插图(如果有的话)、是否有宽大的可写边距、内置书签的数量和种类等等。
很可能一本“典型”的圣经确实是精装的——毕竟,这本书通常是为了反复阅读而设计的,可能会在多个地方做上书签,翻阅时寻找特定的章节和经文等等,而一本好的精装书在这种使用情况下能更耐用。然而,这些都是普通的(实用的)问题,不能用来判断一本实际的书是否是圣经的副本:平装本也是完全可以的!
同样,Python是一种“语言”,在这里是指定义了一类“语言实现”的东西,这些实现必须在某些基本方面相似(语法,大部分语义,除了那些明确允许不同的部分),但在几乎所有的“实现”细节上都是可以不同的——包括它们如何处理给定的源文件,是否将源代码编译成某种低级形式(如果是的话,编译成哪种形式——以及是否将这些编译后的形式保存到磁盘或其他地方),它们如何执行这些形式,等等。
经典的实现,CPython,通常简称为“Python”——但它只是众多高质量实现中的一种,还有微软的IronPython(编译成CLR代码,即“.NET”),Jython(编译成JVM代码),PyPy(用Python本身编写,可以编译成多种“后端”形式,包括“即时”生成的机器语言)。它们都是Python(即“Python语言的实现”),就像许多表面上不同的书籍都可以是圣经(即“圣经的副本”)。
如果你对CPython特别感兴趣:它会将源文件编译成一种特定于Python的低级形式(称为“字节码”),在需要时自动进行编译(当没有与源文件对应的字节码文件,或者字节码文件比源文件旧,或者是由不同版本的Python编译时),通常会将字节码文件保存到磁盘(以避免将来重新编译)。另一方面,IronPython通常会编译成CLR代码(是否保存到磁盘取决于情况),而Jython则编译成JVM代码(是否保存到磁盘——如果保存,会使用.class
扩展名)。
这些低级形式会由相应的“虚拟机”执行,也称为“解释器”——比如CPython虚拟机、.Net运行时、Java虚拟机(也叫JVM),视情况而定。
所以,从这个意义上说(典型实现做什么),Python是一种“解释型语言”,如果C#和Java也是的话:它们都有一个典型的实现策略,即先生成字节码,然后通过虚拟机/解释器执行。
更可能的焦点在于编译过程的“重量”、速度和复杂程度。CPython的设计目标是尽可能快地编译,尽可能轻量,尽量少的复杂性——编译器几乎不进行错误检查和优化,因此它可以快速运行并占用较少的内存,这也使得它在需要时可以自动和透明地运行,用户大多数时候甚至不需要意识到正在进行编译。Java和C#通常在编译时会进行更多的工作(因此不进行自动编译),以便更彻底地检查错误并进行更多优化。这是一个灰度的连续体,而不是黑白分明的情况,随便在某个特定的水平上设定一个阈值,然后说只有超过这个水平的才叫“编译”,这完全是任意的!