静态语言与脚本语言

其实在一开始就会有疑问,什么叫静态语言、脚本语言,Python为什么成了“可以执行的伪代码/脚本”,今天在各处搜索了相关资料,自己尝试总结下。

首先,给定义,采用编译执行的是静态语言,采用解释执行的是脚本语言

编译执行,是必须首先将代码编译成一个目标程序,然后计算机再去执行这个程序的,典型的例子就是自己在Linux环境下练习C语言的时候,编写完.c代码,还要用gcc编译一下成.exe文件,才能够执行。

解释执行,是执行一句,计算机提交一句,不会形成目标程序

在这里就不得不提Java和Python不同的解释器了。

Java先不写

Python代码执行过程

Python解释执行过程,好文

执行过程如下:

1
2
graph TB;
词法分析--> 句法分析 -->编译生成字节码文件-->执行字节码文件

字节码文件即.pyc文件,其实是python虚拟程序中的PyCodeObject,python都是先将代码转换成字节码,然后由虚拟机将字节码变成机器语言,进行执行。

字节码在Python虚拟机程序里对应的是PyCodeObject对象。.pyc文件是字节码在磁盘上的表现形式。简单来说就是在编译代码的过程中,首先会将代码中的函数、类等对象分类处理,然后生成字节码文件。有了字节码文件,CPU可以直接识别字节码文件进行处理,接着Python就可执行了。

为什么要多加一步呢?

我们很容易想到网络协议中的分层思想,其实就是如此,这样一来,python的源代码(你写的那些)离计算机底层更远了,这样你不需要考虑下面的机器环境问题,实现“一次编译,到处运行”(偷偷用Java的话,像是在ntr)

比如:C语言中的生成一个子进程的fork()函数,在windows环境中根本就没有,难道我想用python开多进程的时候还要查一查能不能这样写吗?笑死,根本不需要。你不用在意这些操作系统的小问题,这些都是执行器要做的。

Python编译过后持久化为pyc文件

给大家看个例子:

上图是我为一个项目写的可视化界面,代码量较大,也有很多的文件之间的引用。

而这个是一个简单的B站小爬虫,代码较少

显然,再在前者的__pycache__文件夹里面,就存着一些.pyc文件,这些,其实就是python编译生成的字节码文件。python对于.pyc文件的具体处理可以就看上面的链接文章,一言以蔽之:

Python解释器只把我们可能重用到的模块持久化成pyc文件(其实Python的解释器认为:只有import进来的模块,才是需要被重用的模块。)

这其实就是一个很重要的思维,大家都知道“二八原则”,在代码中也是,20%的代码往往会被反复执行,此时如果能提前直接将这些“高频代码”(即java中的热点代码)编译好,需要执行的时候直接执行不用再解释,岂不美哉!

那我到底是把字节码文件存到内存里,还是要放在磁盘中呢?

OK,我们从实际情况出发,思考下我们在什么时候才可能运行python xxx.py文件:

A. 执行测试时。

B. 开启一个Web进程时。

C. 执行一个程序脚本。

我们逐个来说,第一种情况我们就不用多说了,这个时候哪怕所有的文件都没有pyc文件都是无所谓的。反正你也要从头来一次。

第二种情况,我们试想一个web.py的程序把,我们通常这样执行:

201205140016.jpg

抑或者:

201205140017.jpg

然后这个程序就类似于一个守护进程一样一直监视着8181/9002端口,而一旦中断,只可能是程序被杀死,或者其他的意外情况,那么你需要恢复要做的是把整个的Web服务重启。那么既然一直监视着,把PyCodeObject一直放在内存中就足够了,完全没必要持久化到硬盘上。

最后一个情况,执行一个程序脚本,一个程序的主入口其实很类似于Web程序中的Controller,也就是说,他负责的应该是Model之间的调度,而不包含任何的主逻辑在内,如在http://www.cnblogs.com/kym/archive/2010/07/19/1780407.html中所提到,Controller应该就是一个Facade,无任何的细节逻辑,只是把参数转来转去而已,那么如果做算法的同学可以知道,在一段算法脚本中,最容易改变的就是算法的各个参数,那么这个时候给持久化成pyc文件就未免有些画蛇添足了。

第一天,解决了一个积压很久的问题,还请各位看官指正

为什么要if __name__ ==’__main__‘

2021.2.20

在以前看别人的代码的时候,看到有些人写到if __name__=='__main__',但是自己并没有写还是能够正常运行啊,我自己注意好缩进不就好了,于是并没有很注意,直到有一次在写pygame界面的时候,由于使用了subprocess模块,开了另一个进程,结果出现了奇怪的一幕:

image-20210220213038902

出现了两个界面!!上面的截图显示pygame模块被导入了两次,并且有另外一个一模一样的界面出现了,这就很匪夷所思。

经过思考,最终知道了问题所在:首先,操作系统在开进程的时候,是将父进程的代码和内存区复制一遍,之后再导入子进程的代码的。而 Python属于脚本语言,不像编译型语言那样先将程序编译成二进制再运行,是动态的逐行解释运行。也就是从脚本第一行开始运行,没有统一的入口。所以再导入另一个.py文件作为模块的时候,会将最顶层代码执行一遍的,而我们开子进程的这个操作,就相当于又导入了一遍自己,因此在哪怕没有import别的.py文件的时候,单独启动子进程,还是会出现原代码被重复执行了一次的情况。

if __name__=='__main__',实际上就是在模拟编译型语言,给你个伪程序入口,这样就能够区分咱们的主程序和导入的模块和子进程中的代码区别啦,就不会重复执行了。

参考

(对于子进程和程序入口的知识,还可以再深挖,有点没弄懂本质)