作为Python爱好者,需要了解.py脚本的基本运行机制和特点:在很多任务中,Python的运行过程基本取决于用户,所以源码不需要编译成二进制代码(否则大多数用户友好的功能),同时直接从源代码运行程序。当我们运行一个python文件程序时,Python解释器将源代码转换成字节码,然后解释器执行这些字节码。因此,总的来说,它具有以下三个特点。源码距离底层更远(按照官方文档的解释。不多说了,大家也感受一下)。在运行时,需要由虚拟机生成并执行字节码。(你问我虚拟机在哪里?!你都不看看他们用什么软件执行的!对,就是解释器,别告诉我是IDLE。虚拟机是通过switch-case语句实现的框架函数PyEval_EvalFrameEx,刚才提到的字节码就是本产品执行的。)每次执行脚本时,虚拟机总是有一个额外的加载和链接过程。(所以,比起编译型语言慢了一点,这和“有丝分??裂间期”一样,准备东西也是要花时间的!)那么,有人要问了:“不是说词总是生成的吗?”“在运行时。段码!那么,字节码去哪了?”咳咳,别担心!先说一下虚拟机是如何执行脚本的:完成模块的加载和链接;将源代码翻译成PyCodeObject对象(本产品为字节码),写入内存(方便CPU读取,加快程序运行速度);从上述内存空间中读取指令并执行;程序结束后,决定是否将PyCodeObject写回硬盘(即直接复制到.pyc或.pyo文件);如果稍后再次执行脚本,首先检查本地是否存在上述字节码文件。是则执行,否则重复上述步骤。看!当我们单击(或键入命令)以运行脚本并悠闲地喝咖啡时,“人”虚拟机可以做很多事情。但是,你有没有注意到,生成.pyc还是.pyo文件取决于我们如何运行程序(虽然我们不知道如何运行)。同样,也有人会抱怨:“咦!为什么不直接生成这些文件,这样不是‘更快、更高、更强’吗!”其实虚拟机也是讲究效率的。毕竟对于比较大的项目来说,把PyCodeObject写回硬盘难免要花一些时间,而且你只执行一次它也不知道,然后把刚刚运行完的脚本“丢弃”就完了。然而,它也有甜蜜的一面。比如直接在命令行输入“pythonpath/to/projectDir”(假设projectDir目录下包含“__main__.py”文件和其他需要调用的模块),那么程序运行后,会自动将所有文件保存在当前目录中。该脚本生成一个字节码文件并将其保存在一个新的本地文件夹__pycache__中。(这也可能是写小项目时IDE自动生成.pyc文件的原因,不过问题描述略有歧义,详见上面知乎问题部分)或者,输入“python路径/to/projectDir/__main__.pyonthecommandline》,生成除__main__.py之外的脚本的字节码文件。但总的来说,以上两种行为都大大缩短了项目运行前的准备时间(毕竟分工明确的程序不能太小,复用率也不能太低。除非你满andsupport,somanythings)模块总是在每次导入之前检查自己的字节码文件的修改时间是否和自己一致。如果是,则直接从字节码文件中读取内容,否则,重新导入源模块,最后生成一个同名文件覆盖已有的字节码,从而完成内容的更新(详见import.py).这样就避免了修改源码后与本地字节码文件发生冲突(当然,设计者不会那么傻。)。如果要优化字节码的生成,需要注意以下两点:实现源代码隐藏,并在一定程度上实现反编译。比如Python3.3编译生成.pyc文件,Python3.4就别想跑了!.pyo文件也是经过优化的(注意这两个字,方便后续理解)编译后的程序(比.pyc文件小),同样可以提高加载速度。但对于嵌入式系统,它可以将需要的模块编译成.pyo文件以减少容量。但是总的来说,功能和原来的.py脚本差不多,就是“但不是没用”(当然也不是没用,比如我个人觉得最有用的地方就是防止别人偷看我毕竟.py源文件是以源码的形式直接呈现给大家的。Python所有选项中:-O,表示优化.pyo字节码的生成(这里还有一个词“优化”,注意!)-OO,表示进一步去掉-O选项生成的字节码文件中的docstring(这里是按效果来解释的,不是说从-O选项得到的文件中去掉)py文件生成.pyc文件(以下调用假设/path/to目录下包含.py脚本):python-mpy_compile/path/to/scriptsthatneedtogenerate.pyc.py#Ifbatchprocessing.pyfiles#替换为/path/to/{needtogenerate.pycscript1,script2,...}.py#or/path/to/其效果等价于以下代码:importpy_compilepy_compile.compile(r'/path/to/需要生成.pyc.py的脚本')#也可以是包含.py文件的目录路径#这里尽量使用原始字符串,避免e的麻烦逃逸。例如这里不加“r”,就得对斜杠进行转义。py_compile是Python的内置模块,里面只有两个函数。下面的py_compile.compile(file[,cfile[,dfile[,doraise]]])可以把.py文件编译成.pyc文件(默认),对应的参数解释如下file,表示.pyc或者.pyo需要生成文件的源脚本名(字符串);cfile表示需要生成.pyc或.pyo文件的目标脚本的名称。呃……好像没什么区别(>﹏<),就是源脚本-----【巴拉拉赋予你力量!编译!](* ̄▽ ̄)o─═≡※:☆----->目标脚本。当然,它默认为一串带有.pyc扩展名的路径名(太长了)。此外,当且仅当使用的解释器允许编译为.pyo文件时,它才能以“.pyo”结尾。这就是为什么我在上面的功能解释中添加了“(默认)”一词。dfile,意思是当出现编译错误时,将错误信息中的名称“file”替换为“dfile”。doraise,设置是否忽略异常。如果为True,将抛出PyCompileError异常;否则,错误信息会直接写入sys.stderr(什么!不知道sys.stderr?!提醒:sys.stderr是Python自带的标准错误输出)另外,生成.pyo的格式文件调用如下:python-O-mpy_compile/path/to/需要生成.pyo.py的脚本那么,有人要问了:为什么不使用“python-O/path/to/需要生成.pyo的calledintheformof"script.py"?"忘记了"来解释这一点,很多博客和书籍都像我上面那样解释了"-O"选项的作用,但是详细的解释是-O选项,它优化了.pyc文件(注意我一直强调的“优化”二字,这里用了!)是一个.pyo文件,而不是将.py文件优化编译成.pyo文件。(直接的结果就是优化编译文件比.pyc文件略小,也就是“减重”。现在,大家知道为什么.pyo文件小了吧!)注:以上是生成.pyc还是.pyo文件,会在当前脚本目录下生成一个包含字节码的文件夹__pycache__。可能有人会问,.pyd文件是什么鬼?别担心,它只是一个Python的动态链接库。如果要深入,就不得不涉及到C++的知识。再啰嗦一句:生成字节码的方法有很多种,不只是上面几种。例如,您不妨尝试将上述命令行调用中的“py_compile”更改为“compileall”,并将代码行中的“py_compile.compile”更改为“compileall.compile_file”或“compileall.compile_dir”,或者直接使用IDE具有编译功能生成字节码。再来一句啰嗦:了解Python的运行机制,对于我们普通人来说是没有必要的(吃瓜群众:“滚!我刚看完,你就说了?!”)。但是了解它加速程序运行和优化代码的设计思想,对我们以后构建缓存系统,如何减少不必要的运行时间,同步更新工作内容等问题都有很大的帮助。如果想了解更多,可以浏览官方文档和其他博客:https://docs.python.org/3.5/u...https://docs.python.org/3.5/l...https//docs.python.org/3.5/c...http://nedbatchelder.com/blog...http://www.tuicool.com/articl...http://developer.51cto.com/ar...
