哈喽大家好,我是咸鱼
不知道有没有小伙伴跟我一样,刚开始学习 Python 的时候都听说过 Python 是一种解释型语言,因为它在运行的时候会逐行解释并执行,而 C++ 这种是编译型语言
不过我今天看到了一篇文章,作者提出 Python 其实也有编译的过程,解释器会先编译再执行
不但如此,作者还认为【解释】与【编译】是错误的二分法、限制了编程语言的可能性。
Python 在运行任何代码之前仍然执行一些编译阶段,就像 Java一样,它会把源码编译成字节码
前面三个报错是 Python 在编译阶段产生的,只有最后一个才是在运行时产生,即ZeroDivisionError: division by zero
.
实际上,我们可以使用命令行上的 compileall
模块预先编译所有 Python 代码:
$ python3 -m compileall
这会将当前目录中所有 Python 文件的编译字节码放入其中 __pycache__/
,并显示任何编译器错误
如果你想知道那个 __pycache__/
文件夹中到底有什么,我为 EdmontonPy 做了一个演讲,你应该看看!
演讲地址:https://www.youtube.com/watch?v=5yqUTJuFuUk&t=7m11s
只有在 Python 被编译为字节码之后,解释器才会真正启动,我希望前面的练习已经证明 Python 确实可以在运行时之前报错
编译语言与解释语言是错误的二分法
每当一种编程语言被归类为【编译】或【解释】语言时,我都会感到很讨厌。一种语言本身不是编译或解释的
一种语言是编译还是解释(或两者兼而有之!)是一个实现细节
我不是唯一一个有这种想法的人。Laurie Tratt 有一篇精彩的文章,通过编写一个逐渐成为优化编译器的解释器来论证这一点
文章地址:https://tratt.net/laurie/blog/2023/compiled_and_interpreted_languages_two_ways_of_saying_tomato.html
还有一篇文章就是 Bob Nystrom 的 Crafting Interpreters。以下是第 2 章的一些引述:
当你使用 CPython 来运行 Python 程序时,源码会被解析并转换成内部字节码格式,然后在虚拟机中执行
从用户的角度来看,这显然是一个解释器(因为它们从源码运行程序),但如果你仔细观察 CPython(Python 也可译作蟒蛇)的鳞状表皮(scaly skin),你会发现它肯定在进行编译
答案是:CPython 是一个解释器,它有一个编译器
那么为什么这很重要呢?为什么在【编译】和【解释】语言之间做出严格的区分会适得其反?
【编译】与【解释】限制了我们认为编程语言的可能性
编程语言不必由它是编译还是解释来定义的!以这种僵化的方式思考限制了我们认为给定的编程语言可以做的事情
例如,JavaScript 通常被归入“解释型语言”类别。但有一段时间,在 Google Chrome 中运行的 JavaScript 永远不会被解释——相反,JavaScript 被直接编译为机器代码!因此,JavaScript 可以跟上 C++ 的步伐
出于这个原因,我真的厌倦了那些说解释型语言必然慢的论点——性能是多方面的,并且不仅仅取决于"默认"编程语言的实现
JavaScript 现在很快了、Ruby 现在很快了、Lua 已经快了一段时间了
那对于通常被标记为编译型语言的编程语言呢?(例如 C)你是不会去想着解释 C 语言程序的
语言之间真正的区别
语言之间真正的区别:【静态】还是【动态】
我们应该教给学生的真正区别是语言特性的区别,前者可以静态地确定,即只盯着代码而不运行代码,后者只能在运行时动态地知道
需要注意的是,我说的是【语言特性】而不是【语言】,每种编程语言都选择自己的一组属性,这些属性可以静态地或动态地确定,并结合在一起,这使得语言更“动态”或更“静态”
静态与动态是一个范围,Python 位于范围中更动态的一端。像 Java 这样的语言比 Python 有更多的静态特性,但即使是 Java 也包括反射之类的东西,这无疑是一种动态特性
我发现动态与静态经常被混为一谈,编译与解释混为一谈,这是可以理解的
因为通常使用解释器的语言具有更多的动态特性,如 Python、Ruby 和 JavaScript
具有更多静态特性的语言往往在没有解释器的情况下实现,例如 C++ 和 Rust
然后是介于两者之间的 Java
Python 中的静态类型注释已经逐渐(呵呵)在代码库中得到采用,其中一个期望是:由于更多静态的东西,这可以解锁 Python 代码中的性能优势
不幸的是,事实证明,Python 中的类型(是的,只是一般类型,考虑元类)和注释本身都是Python 的动态特性,这使得静态类型不是大伙所期望的性能优势
最后总结一下:
- CPython 是一个解释器,它有一个编译器(或者说 Python 既是解释型语言,也是编译型语言)
- Python 是编译的还是解释的并不重要。重要的是,相对于那些具有更多静态属性(在编译或解释阶段可以在运行前确定的属性)的编程语言,Python 中可以在运行前确定的属性相对较少,这意味着在 Python 中,许多属性是在运行时动态确定的,而不是在编译或解释时静态确定的
- 由于 Python 具有较少的静态属性,这意味着在运行时,某些错误可能只能在运行时才会显现,而不是在编译或解释时就能被发现
- 因为在具有更多静态属性的编程语言中,许多错误会在编译或解释时被捕获,因此更容易在编码阶段就发现和修复
- 这是真正重要的区别,这是一个比【编译】和【解释】更细致、更微妙的区别。出于这个原因,我认为强调特定的静态和动态特性是很重要的,而不是一昧的局限于“解释型”和“编译型”语言之间的繁琐的区别。