作者:知乎用户
链接:https://www.zhihu.com/question/30296617/answer/112564303
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
作为Python爱好者,需要了解.py脚本的基本运行机制及特性:
在很多工作上Python的运行流程基本上取决于用户,因此源码不需要编译成二进制代码(否则无法实现大部分贴近用户的特性),而直接从源码运行程序。当我们运行python文件程序的时候,Python解释器将源码转换为字节码,然后再由解释器来执行这些字节码。因此总的来说,它具有以下三条特性
那么,有人要问了:“不是说,运行时总要生成字节码么!那,字节码都去哪儿了?” 咳咳,别急!容我先说说,虚拟机它是怎么执行脚本的(咕噜咕噜喝杯水…):
你看!在我们点击(或输入命令)运行脚本,并悠闲地喝咖啡时,“人家”虚拟机做了这么多的事情。不过,你有没有发现.pyc或.pyo文件是否生成,是取决于我们如何运行程序的(虽然我们不知道要怎么做(ง •̀_•́)ง )。
同样,有人会吐槽:“哼!为什么不直接生成这些文件,这样来得不是‘更快、更高、更强’!”
其实,虚拟机也是讲究效率的。毕竟对于比较大的项目,要将PyCodeObject写回硬盘也是不可避免地要花些时间的,而且它又不知道你是不是也就只执行一次,之后就对刚刚跑完的脚本“弃之不顾”了呢。不过,它其实也有贴心的一面。比如,
或者是,在命令行输入“python path/to/projectDir/main.py”,则生成除main.py外脚本的字节码文件。不过总的来说,上述这两种行为都大大缩短了项目运行前的准备时间(毕竟分工明确的程序,规模应该不会太小,复用率也不会太低。除非吃饱了撑着,搞出这么多事情(Θ皿Θメ))
若想优化生成字节码,应注意这两点:
但总的来说,作用上是几乎与原来的.py脚本没有区别的,也就是“然并卵 ”(当然,并非毫无作用。比如,我个人觉得用处最大的地方就是防止别人偷看我的代码。(:з」∠)毕竟.py源文件是直接以源码的形式呈现给大家的)。╮(╯▽╰)╭ 呃…这么说,好像又有点自相矛盾的赶脚。
在所有的Python选项中:
对此,我们可以使用如下格式运行.py文件来生成.pyc文件(以下调用均假设/path/to目录含有.py脚本):
其效果等效于如下代码:
py_compile是Python的自带模块,这里面就两个函数(看到这个,我笑了(๑•́ ₃ •̀๑)噗噗)。其下的py_compile.compile(file[, cfile[, dfile[, doraise]]])可将.py文件编译生成.pyc文件(默认),对应的参数解释如下
(╯’ - ‘)╯︵ ┻━┻ (掀桌子) ┬─┬ ノ( ’ - ‘ノ) (摆好摆好) (╯°Д°)╯︵ ┻━┻(再TA喵掀一次)
另外,生成.pyo文件的格式调用如下:
那么,有人要问了:为什么不是像生成.pyc文件那样采用“python -O /path/to/需要生成.pyo的脚本.py”形式的调用?
“忘记”说明这一点了,很多博客以及书籍都像我上面那样解释“-O”选项的作用,但详细来解释的话是
-O选项,将.pyc文件优化(注意我一直强调的“优化”二字,这里就用到啦!)为.pyo文件,而不是将.py文件优化编译为.pyo文件。(其直接的结果是优化编译后的文件略微小于.pyc文件,也就是“减肥”了。现在,大家知道.pyo文件为什么小的原因了吧!)
注意:
以上无论是生成.pyc还是.pyo文件,都将在当前脚本的目录下生成一个含有字节码的文件夹pycache。
可能还有人会问,.pyd文件又是什么鬼(>﹏<)?!(问题真多,精分ing…) 别在意,那只是Python的动态链接库。如果要深究,还得扯上C++的知识(长篇大论的,会被喷的啊)。
再啰嗦一句:生成字节码的方法多了去了,不止以上这几种。比如,你们不妨试试将上面命令行调用中的“py_compile”改成“compileall”,而代码行中的“py_compile.compile”改成“compileall.compile_file”或“compileall.compile_dir”,又或者直接使用带有编译功能的IDE生成字节码。
再再啰嗦一句:知道Python运行机制,并不是我们一般人所必须的(吃瓜群众:“滚!我刚好不容易看完了,你才说?!”)。但是,了解其加速程序运行以及优化代码的设计思想,对于我们在日后构造缓存系统、如何减少不必要的运行时间,以及同步更新工作内容等问题上起到很大的借鉴作用。
若想要了解更多的内容,可以去翻翻官方文档和其他博客: