11
5 月
从0开始的Python教程00:代码是如何运行的
- By IanGoo
身处数据时代,数据分析已经逐渐成为很多工作当中的必备技能。而随着数据规模的扩大,很多办公室民工已经比较熟悉的Excel疲态尽显,功能和性能都已经无法跟上要求。从程序设计的思路进行数据分析就显现出了优势所在。专门的数据科学领域,有两个最常用的工具——R和Python。R算是根正苗红的数据分析工具,但是Python的应用更加广泛,学会了之后甚至可以写个网站什么的,而且入门门槛更低。所以,我还是选择从Python入手。
但是作为设计师和十六分之一桶水程序员,我也深知让一个完全不懂写程序——甚至不太习惯程序化思维的人去系统性学习一门程序语言的困难。于是,本系列将完全从0开始。
什么是程序语言?
程序猿是一种神奇的生物,他们平时沉默寡言,只是按部就班地解决着手头的问题,编号喜欢从0开始(如同本文一样),1024对于他们来说是个整数,对于他们来说,最大的冒犯也许是将他们手下的35g HHKB静电容键盘抢走换成一把60g黑轴机械键盘,但是他们也最多嘟囔两声以表抗议,然后用更多的代码再买一把35g HHKB键盘。
但是有两件事情,足够起到在动物园的猴山里丢一把香蕉的效果,引发程序猿猩球的世界大战——
- XXXX是最好的编辑器!
- XXXX是最好的编程语言!
第一个问题其实当量并不算太大,因为不多久就会只剩Vim和Emacs这两个最大、最古老的帮派坚持打架斗殴,其他的UltraEdit、Visual Studio Code、EmEditor之类的年轻帮派往往在一旁吃着香蕉看戏。但是第二个问题,足以引发真正意义上的世界大战——没有人能够独善其身。
//以上为段子
这个时代,就算不是程序猿,也肯定听说过“程序语言”这个东西。甚至很多人为了对付大学毕业必备的全国计算机等级考试(NCRE)二级,而学过一些程序设计。但是那些看起来佶屈聱牙的“代码”,为什么要如此设计呢?
其实根源我们在《MBSE概述》一文当中已经介绍过了——电脑还太笨,只能理解以一定范式编写的规范化的、模型化的语言。我们要电脑干一件事情必定要先“告诉”电脑要干嘛,尽管今天我们有Siri,有小爱同学,有Cortana,但是用过的都知道它们能干的事情非常有限。因为人类之间交流使用的是自然语言,让计算机系统理解大段、复杂自然语言目前还是有很多困难的。因此,高度规范化的程序语言就成了“告诉电脑做某件事情”的最有效的方法。从这种层面上说,程序猿其实某种程度上等同于翻译官——将人类语言“翻译”成程序语言交给电脑执行的翻译官。
前面提到的全国计算机等级考试在2018年将Python纳入了考试项目,老资格的Visual Basic则在2021年3月被踢出了考试。目前,在NCRE二级考试当中“程序设计”这一科目当中,有C、C++、Java和Python这四门,Python是其中第一个也是目前唯一一个典型的解释型语言。
那什么又是“解释型语言”?
什么是解释型语言?
程序语言已经是高度规范化了,只需要让电脑执行一段代码,如果没有Bug就应该能顺利运行并且得到想要的结果。但是,“让电脑执行一段代码”这几个字看起来轻轻松松,实际上却内有乾坤。
计算机本身也不是铁板一块,分成硬件、软件,硬件当中的核心就是号称计算机的大脑的处理器,也是实际上帮我们解决问题、进行计算的设备。但是问题在于,CPU其实根本看不懂代码!它能理解的只是一串0和1构成的代码,称为机器码。

如果说谁对计算机的这个特性有深刻的感受的话,上世纪60-70年代的程序员应该当仁不让,程序员需要在卡片上打孔来标记0或者1,通过这样的方式直接“手写机器码”喂给计算机,计算机才能干活。
用这种方式写程序当然很烧脑,这就是计算机发展史上的第一代程序语言。随着时间的发展,顶尖大脑们总结出了一些规律——总有一些很常用的指令需要重复地去打孔,干嘛不把这些常用指令固化下来成为标准呢?一个重要的概念诞生了——指令集(Instruction Set)。指令集是硬软件同步创新的结果,从硬件层面上说,一些常用的操作所使用的电路组合被固化下来,从软件层面上说,这些固化的电路组合对应的指令可以直接以人类能理解的方式写出来并形成标准,以这套标准衍生出来的新的写程序的方式,叫做汇编语言,这是第二代程序语言。如简单地做一个乘法,以往需要哐哐哐打一堆小窟窿眼,现在只需要三个字母就搞定了——MUL
,而且很好记,乘法——Multiply的前三个字母。汇编代码写完,通过汇编器可以迅速转换为机器码交给处理器执行。
但是需要说明,汇编语言只是在机器码基础上进行的简单的翻译,属于“人能看得懂的机器码”,所以它需要硬件的支持。如果处理器的指令集里压根就没有某个指令,或者指令的语法不同,程序就根本就跑不起来。为什么我们没办法在iPhone上直接运行PC的程序?除了操作系统层面的原因,更为根本的原因就是两者的硬件平台使用的指令集完全不同,目前多数手机使用的是ARM指令集,而电脑则是X86指令集。对于使用汇编代码写程序的人来说,这样的不方便也是显而易见的——辛辛苦苦写了一个程序,换了设备就有可能用不了,这在计算机行业有一个评语——可移植性(Portability)太差。
于是又有一群顶尖大脑想了个办法,将汇编语言再次进行抽象,形成了一套不依赖于设备的标准。这就是今天我们所熟悉的高级程序语言。我们今天接触到的绝大多数“代码”——不论是入门语言BASIC、随心所欲C++还是中文之光易语言,都属于高级程序语言[2]。高级程序语言的代码写完后,通过软件层面上的编译器将其编译为汇编语言,然后汇编语言再通过汇编器汇编为机器码执行。
我们总结一下代码开始运行的这个过程,大致是这样:

事实上,任何一种程序从代码到运行都需要经历这样的过程,所以,编译这个过程其实在任何高级程序语言的实现上都是存在的。那么为什么又说Python是“解释型语言”呢?
写过程序的应该都知道,程序写完之后,点一下编译,计算机就开始了一顿猛如虎的操作,最后会生成一个可执行文件,内含可以直接由机器运行的二进制数据,也就是大家很熟悉的在Windows系统里后缀是.exe
的东西。
有了可执行文件,编译器就不再需要了。比如Apache官方发布的Windows版httpd服务器软件,发布的是使用Visual C++ 2015或者Visual Studio 2016编译出来的可执行文件,用户可以下载下来在Windows操作系统上直接使用,而不必安装VC2015或者VS2016。
这种方式运行代码就是编译型语言的典型特征。
而解释型语言则完全不一样了。如我们这里介绍的Python,Python代码是以.py
的后缀保存的,这玩意儿其实就是一个换了后缀的纯文本文件,和.txt
其实没有任何区别。但是,Python有一个运行环境——Python解释器。想要运行Python程序,就必须在所使用的操作系统上安装Python解释器,解释器加载.py
文件,一边按需编译里面的代码一边运行,整个过程中不会有可见的、类似编译型语言那种将整个代码全部编译成可执行文件的这一步。
按照这种方式运行代码的就是解释型语言的典型特征。
总结一下如下图:

编译和解释各有千秋,编译型语言效率和传播性显然优于解释型语言,一次编译完成后,便不再需要编译器,而且由于直接就可以运行,效率比每次都需要转换成机器码运行的解释型语言高得多。同时,解释型语言的运行需要安装解释器,比起轻装上阵啥都不用的编译型语言多了一层阻力。但是解释型语言胜在灵活。如果源代码发生了变更,编译型语言需要将所有代码全部重新编译一遍,形成新的可执行程序来运行——哪怕这个变更的范围非常小。而解释型语言则不用,甚至解释型语言在运行的时候本来就是按需解释的,用不上的语句在运行时不会解释。另外,跨操作系统发布的时候,由于可执行文件的结构和操作系统高度相关,一般不能不做任何处理直接运行,这就使得编译型语言写成的软件在跨平台分发的时候面临两难境地——要么开发者自己一顿编译猛如虎,将所有操作系统的版本预先编译好,要么开发者直接源码发布,交给用户自己去编译。前者要消耗开发者的时间,后者会让用户望而却步,甚至用户根本不具备编译软件的条件。Linux有一个神奇的发行版叫Gentoo,系统更新非常奇葩——Debian、Ubuntu这种比较正常的发行版更新一个软件就是直接下载编译好的程序文件回来替换,这玩意儿则是下载源码回来本地编译,当年我仗着笔记本还算性能比较强,装完了Gentoo之后上手就更新一下Firefox浏览器,结果电脑以满负荷状态运行了将近一个小时,编译的过程当中内存占用6个多G,硬盘疯狂读写,要是换成我现在使用的Surface Pro 7肯定就横尸当场。而解释型语言,直接放更新后的源代码就行了。虽然运行的效率差一些,但是反正运行的时候并不会解释全部的代码,倒也不至于出现编译Firefox那样的惨烈场面。
OK,写到这里该插入一些题外话了,注!意!虽然一般都认为C、C++这类属于“编译型语言”而Python属于“解释型语言”,但是“编译型”和“解释型”并不是一个很严谨的分类法。原因如下:
第一,如前文所说,就算是解释型语言,一样要经过代码→汇编→机器码的这个过程,它其实同样存在编译的过程,只不过这个过程是动态的、按需的,人看不到这个过程中生成的可执行文件,从这个层面上理解,两者没有本质区别,只是执行时间、动态/静态的区别。
第二,“编译”和“解释”,针对的是代码的运行方式而不是代码本身。我们之所以说C、C++是编译型,仅仅是因为它们大多数的实现都是以编译的方式运行代码的。事实上,C、C++的解释器已经很多了,比如Ch、PicoC等等,一些C编译器同样具备以解释器的方式运行C代码的能力,如TCC。另一方面,Python同样具备编译成可执行文件的功能。
第三,别忘了还有Java和C#这两个奇葩。一般都说这哥俩是“半解释半编译”,但是这种说法连我这个门外汉都觉得没谱。Java同样需要“编译”,但是官方的实现输出的不是机器码,而是字节码,然后再用专门的虚拟机(JVM)去解释字节码。
所以,不必太纠结“Python是解释型语言”这句话,只需要明白一件事——台式机的Windows和笔记本的Linux上都装了Python解释器,在台式机上用随便什么文本编辑器写的.py
文件拷贝到笔记本上照样可以运行。而要修改功能可以直接用任意文本编辑器打开.py
文件修改,而不必重新编译。这也是Python受欢迎的一个重要原因。
不必纠结还写了这么长一段?
自言自语
Emm……这是为了介绍代码的运行方式的所以写得多了一些。
由于Python解释器的选择非常多样,官方的解释器也是跨平台的,只要不是什么特别奇葩平台都能找到对应的Python解释器——就算是奇葩平台,找到Python解释器也不是很难的事情,这就赋予了Python非常广泛的应用范围,再加上Python低门槛的特性,它的流行也就可以理解了。
