图文教案:为什么后端开发系列课程选择了 Go 和 Python(视频课程7)

写在前面

《从0到1学习后端开发》是面向在校大学生、非 IT 工作人员的免费后端开发入门实践教程,可至 https://space.bilibili.com/425571569 查看对应的视频资料。

为什么是 python 和 go 这两门语言

作为一个系列课程,可能有必要详尽地说明一下,为什么选择的是 python 和 go 这两门语言;有的同学可能更希望选定的是 java、Rust、C/C++ 等其他语言。

在前面的课程里有提到过, python 是我目前工作的主要编程语言, go 是我曾经主力使用且一直跟踪其发展的编程语言。而且站在一个稍微有点经验的程序员的视角,不存在最好的语言,存在最适合的语言。作为一名中高级的开发者,大家会发现自己平日里的工作所解决的困难都是业务相关的复杂度,编程语言的迁移(假如真的存在这种情况)相对反而会是比较容易的,只要团队里有比较靠谱的人带队,从入门到有产出花不了太多时间。

不过,根据 TIOBE 指数 java 依然是最流行的工业级编程语言,其热度长期占据排行榜首位,大家可以自行斟酌吧。

简单对比go/python/java的模型

在正式开始 python 和 go 的学习之前,让我们首先简单地比较一下几种编程语言的模型,这里我选择了比较有代表性的 go、python 和 java。

几种编程语言的模型

我们看一下各个编程语言“从源码到机器执行”所经历的环节。大家都是从源码开始,最终都是电脑的 CPU 和 内存来承载执行源码对应的逻辑。

首先我先给大家注点鸡血,程序员写代码控制的主要是 CPU 和 内存,它们属于机器的一种;挖掘机或其他的车也是机器的一种,如果我们能够合理地摆弄那几个操纵杆学会开车,那么没有理由学不会开电脑。唯手熟尔,仅此而已。

go语言

让我们首先看一下 go 语言。

go 诞生自 2009 年,和 python(1991)、java(1995)比起来算是一门比较新的语言,理论上具有后发优势,语言特性会比较先进。这也是建议大家学习它的一个原因。

go 语言和 c 语言类似,经过编译后会生成可直接执行的二进制文件。图中使用“很快地编译“,是因为 go 语言编译起来非常快,这也是 go 语言涉及之处的一个目标;我们可以拿另一个比较新且流行的编程语言 Rust 简单对比,go 的编译速度比 rust 可以快一个数量级。举个例子,如果 Rust 项目编译使用 30 分钟(TiKV 项目),那么 go 语言复杂度相当的项目(TiDB)可能三分钟就构建完成了。

go 编译生成的二进制文件可以直接被加载到机器上运行,也就是说,go 编译生成的二进制文件直接是机器可识别的内容(或者说,go 编译生成的汇编代码就是 CPU 指令集对应的代码)。这与 python 和 java 是完全不同的。

java 语言

java 生态里非常引人注目的是 JVM(java virtual machine,java虚拟机)。对于虚拟机,大部分人的认知可能局限在 virtualBox、VMware 等,本课程最初也介绍了在虚拟机 virtualBox 上安装 Ubuntu 系统的具体步骤。我们可以把虚拟机认为是一种对硬件的抽象,基于我们电脑硬件的 CPU 的指令集,模拟出特定指令集合的计算单元。同样地,java 虚拟机也对硬件 CPU 和内存资源进行了抽象,而且抽象后可以运行 java 字节码。

java 源码编译后生成 java 字节码,字节码由 java 虚拟机的指令集组成,可以在 JVM 平台执行。

我们经常见到的 Jar 包和 War 包都是字节码文件包,是源码编译后的产物。相对 go 来说, java 的编译过程要慢很多,常常会让电脑卡顿好一会儿。

简单讲,java 从源码到执行的过程是: 源码中的 1+1 语句,首先被编译成为 JVM 的命令(字节码),然后字节码由 JVM 加载,并被 JVM 转义成为 CPU 的指令从而得以执行。CPU 运行 java 代码时,我们的 CPU ①一边做字节码到CPU指令的转义,②一边运行实际转义后的语句。相对于 go 语言只运行CPU指令的情况, java 的 CPU 使用效率会低一些。

不过 java 作为一门工业级的编程语言,很多大牛为 JVM 的优化做了很多的事情,这也使得基于 JVM 的编程语言的运行效率与裸 CPU 的编程语言的运行效率差距没那么大了。因此,在 JVM 的平台上,还诞生了 kotlin 、Jython 这种编程语言,它们都是编译生成 JVM 指令集的字节码,然后在 JVM 上运行。

python 语言

使用 python 开发,从源码到机器执行并没有显式地编译过程,只需把源码加载到解释器就可以正常运行了。

就像前面提到的,类似 1+1 这样的源码是不可能直接在 CPU 上执行的,那么 python 源码是如何运行在机器上的呢?实际上 python 解释器会在内部把 python 源码编译形成 python 字节码,然后再把这些字节码加载到解释器的运行时(python 虚拟机)进行执行。

除了“隐式编译”这个步骤,python 几乎与 java 是一样的。而且 python 解释器只会在第一次加载运行 python 源码的时候存在“隐式编译”这个步骤,并且把编译生成的 python 字节码保存为 .pyc 文件,再以后执行时会首先加载 pyc 文件,从而省去了重复编译的步骤。

既然 python 与 java 类似,那么为什么我们经常听到大家讲“python运行速度慢”而很少听到“java执行速度慢”呢?这其实涉及到语言另外的问题。

python 执行速度慢的三点原因

首先应该科普一下:80%的业务还没跑到编程语言出现性能瓶颈就被淘汰了。因此有时候偏执执行效率并不睿智,“不存在最好的语言,存在最适合的语言”就是这个道理。

① 首先看静态语言与动态语言的区别。静态语言的类型检查在编译时进行,而动态语言的类型检查在运行时进行。java 是一种静态语言,其类型检查在我们编译 java 源码时就完成了;比如当有类型赋值错误时,会直接编译失败。而 python 是一种动态语言,其类型检查放在了运行时,因此会占用运行时的计算资源,导致执行速度慢。

② 另一方面,为了便利性 python 的数据类型的粒度取的比较粗(比如整型不区分精度,可以表示任意大的值),而 CPU 固有的数据位(单次读写最大位数)有限,在转换过程中会存在性能损耗。

③ python 在 1991 年诞生时,CPU 是单核的,因此语言模型对多核的支持不好。在 python 虚拟机(运行时)中,存在一个 GIL(Global interpreter lock,解释器全局锁),限制一个解释器同一时间只能转义一条字节码为机器码,无疑这会限制 python 对多核的使用。

对技术(包括编程语言)怀有敬畏之心

发现很多人因为各种原因忘了初心,达到某个水平后不再精进自己。希望我们都可以心怀敬畏,不断进取吧。

小结

本文简单介绍了 go、java、python 的语言模型。介绍了本系列课程选择 python 和 go 两种语言的理由。

参考