Python运行机制以及pyc/pyo文件的作用

作者:知乎用户
链接:https://www.zhihu.com/question/30296617/answer/112564303
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

作为Python爱好者,需要了解.py脚本的基本运行机制及特性:

在很多工作上Python的运行流程基本上取决于用户,因此源码不需要编译成二进制代码(否则无法实现大部分贴近用户的特性),而直接从源码运行程序。当我们运行python文件程序的时候,Python解释器将源码转换为字节码,然后再由解释器来执行这些字节码。因此总的来说,它具有以下三条特性

  • 源码距离底层更远(根据官方文档的解释。不说,你们也感觉得到)(。・`ω´・)
  • 运行时都需要生成字节码,交由虚拟机执行。(你们问我虚拟机在哪儿?!你们也不看看各自都是用什么软件执行的!没错,就是解释器,别和我说是IDLE啊。虚拟机具体实现了由switch-case语句构成的框架函数PyEval_EvalFrameEx,刚刚说的字节码就是这货执行的)
  • 每次执行脚本,虚拟机总要多出加载和链接的流程。(所以呢,相比于编译型语言就有点慢了。这与“有丝分裂间期”一样,准备东西也要花时间啊!)

那么,有人要问了:“不是说,运行时总要生成字节码么!那,字节码都去哪儿了?” 咳咳,别急!容我先说说,虚拟机它是怎么执行脚本的(咕噜咕噜喝杯水…):

  • 完成模块的加载和链接;
  • 将源代码翻译为PyCodeObject对象(这货就是字节码),并将其写入内存当中(方便CPU读取,起到加速程序运行的作用);
  • 从上述内存空间中读取指令并执行;
  • 程序结束后,根据命令行调用情况(即运行程序的方式)决定是否将PyCodeObject写回硬盘当中(也就是直接复制到.pyc或.pyo文件中);
  • 之后若再次执行该脚本,则先检查本地是否有上述字节码文件。有则执行,否则重复上述步骤。

你看!在我们点击(或输入命令)运行脚本,并悠闲地喝咖啡时,“人家”虚拟机做了这么多的事情。不过,你有没有发现.pyc或.pyo文件是否生成,是取决于我们如何运行程序的(虽然我们不知道要怎么做(ง •̀_•́)ง )。

同样,有人会吐槽:“哼!为什么不直接生成这些文件,这样来得不是‘更快、更高、更强’!”
其实,虚拟机也是讲究效率的。毕竟对于比较大的项目,要将PyCodeObject写回硬盘也是不可避免地要花些时间的,而且它又不知道你是不是也就只执行一次,之后就对刚刚跑完的脚本“弃之不顾”了呢。不过,它其实也有贴心的一面。比如,

  • 若你在命令行直接输入“python path/to/projectDir”(假设projectDir目录含有“main.py”文件,以及其他将要调用的模块),那么程序运行结束后便自动为当前目录下所有的脚本生成字节码文件,并保存于本地新文件夹pycache当中。(这也有可能是IDE写小项目时自动生成.pyc文件的原因,不过问题描述略微暧昧。详情参见上面知乎问题板块)

或者是,在命令行输入“python path/to/projectDir/main.py”,则生成除main.py外脚本的字节码文件。不过总的来说,上述这两种行为都大大缩短了项目运行前的准备时间(毕竟分工明确的程序,规模应该不会太小,复用率也不会太低。除非吃饱了撑着,搞出这么多事情(Θ皿Θメ))

  • 模块在每次导入前总会检查其字节码文件的修改时间是否与自身的一致。若是则直接从该字节码文件读取内容,否则源模块重新导入,并在最后生成同名文件覆盖当前已有的字节码,从而完成内容的更新(详见import.py)。这样,就避免了修改源代码后与本地字节码文件产生冲突(当然,设计者也不会这么傻。╮( ̄▽ ̄”)╭)。

若想优化生成字节码,应注意这两点:

  • .pyc文件是由.py文件经过编译后生成的字节码文件,其加载速度相对于之前的.py文件有所提高,而且还可以实现源码隐藏,以及一定程度上的反编译。比如,Python3.3编译生成的.pyc文件,Python3.4就别想着去运行啦!→_→.
  • pyo文件也是优化(注意这两个字,便于后续的理解)编译后的程序(相比于.pyc文件更小),也可以提高加载速度。但对于嵌入式系统,它可将所需模块编译成.pyo文件以减少容量。

但总的来说,作用上是几乎与原来的.py脚本没有区别的,也就是“然并卵 ”(当然,并非毫无作用。比如,我个人觉得用处最大的地方就是防止别人偷看我的代码。(:з」∠)毕竟.py源文件是直接以源码的形式呈现给大家的)。╮(╯▽╰)╭ 呃…这么说,好像又有点自相矛盾的赶脚。

在所有的Python选项中:

  • -O,表示优化生成.pyo字节码(这里又有“优化”两个字,得注意啦!)
  • -OO,表示进一步移除-O选项生成的字节码文件中的文档字符串(这是在作用效果上解释的,而不是说从-O选项得到的文件去除)
  • -m,表示导入并运行指定的模块

对此,我们可以使用如下格式运行.py文件来生成.pyc文件(以下调用均假设/path/to目录含有.py脚本):

python -m py_compile /path/to/需要生成.pyc的脚本.py #若批量处理.py文件
                                                  #则替换为/path/to/{需要生成.pyc的脚本1,脚本2,...}.py
                                                  #或者/path/to/

其效果等效于如下代码:

import py_compile
py_compile.compile(r'/path/to/需要生成.pyc的脚本.py') #同样也可以是包含.py文件的目录路径
                                                    #此处尽可能使用raw字符串,从而避免转义的麻烦。比如,这里不加“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自带的标准错误输出)

(╯’ - ‘)╯︵ ┻━┻ (掀桌子) ┬─┬ ノ( ’ - ‘ノ) (摆好摆好) (╯°Д°)╯︵ ┻━┻(再TA喵掀一次)

另外,生成.pyo文件的格式调用如下:

python -O -m py_compile /path/to/需要生成.pyo的脚本.py

那么,有人要问了:为什么不是像生成.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运行机制,并不是我们一般人所必须的(吃瓜群众:“滚!我刚好不容易看完了,你才说?!”)。但是,了解其加速程序运行以及优化代码的设计思想,对于我们在日后构造缓存系统、如何减少不必要的运行时间,以及同步更新工作内容等问题上起到很大的借鉴作用。

若想要了解更多的内容,可以去翻翻官方文档和其他博客:

贴心提示:以上颜文字以及搞怪语气,主要是为了能够提高各位的阅读兴趣。如有不当的地方,恳请各位能够悉心指出。

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 在线pycpyo反编译是指通过在线工具或网站将Python编译后的.pyc或.pyo文件转换回可读的源代码的过程。Python源代码在运行之前会被编译成字节码(.pyc或.pyo文件),这样可以提高执行效率。然而,由于.pyc和.pyo文件并非完全加密,因此可以通过反编译工具将其转换回可读的源代码。 在线pycpyo反编译通常使用反编译器工具来实现。这些工具可以读取.pyc或.pyo文件的字节码,并将其转换回源代码形式。有些工具甚至还可以还原出原始的变量名称和代码结构,使得反编译的源代码与原始源代码非常接近。 在线pycpyo反编译通常用于以下情况: 1. 学习目的:对于学习Python编程的人来说,通过反编译可以获得其他人编写的代码的思路和技巧,加快学习过程。 2. 代码审查:在某些情况下,需要对他人编写的代码进行审查和分析,以确保代码的安全性和质量。反编译工具可以帮助审查人员更好地理解代码的功能和实现。 3. 调试目的:有时,在调试Python程序时,通过查看反编译后的代码,可以更好地理解程序的执行逻辑和问题所在。 需要注意的是,虽然在线pycpyo反编译可以将编译后的文件转换回源代码形式,但生成的源代码并非完全和原始源代码一致。编译过程中存在一些优化和变换,有些信息可能会丢失。另外,反编译出的代码可能会被修改过,不一定能够完全反映原始代码的意图。 总之,在线pycpyo反编译是一种将Python编译文件还原为源代码的方法,常用于学习、审查和调试等目的。 ### 回答2: 在线pyc,pyo反编译是指通过在线工具或平台对Python编译后的二进制文件(.pyc或.pyo)进行逆向操作,将其转化为可读的源代码。反编译可以帮助开发人员了解某个Python程序的工作原理以及内部实现细节。 反编译对于开发人员来说是一个很有用的工具,它可以帮助我们理解某些已编译的Python代码,无论是为了学习、调试还是分析代码,都可以通过反编译来获得更多的信息。 值得注意的是,反编译只适用于已经编译成二进制文件Python代码,而对于普通的.py源代码文件是不需要进行反编译的。当我们使用在线的反编译工具时,我们需要上传或指定相应的二进制文件,工具会将其反编译成Python源代码。 然而,需要注意的是,反编译得到的源代码可能不完全等同于源代码文件,因为编译过程中可能会有一些优化和变换。此外,反编译也可能会面临一定的法律和伦理问题,因此在使用反编译工具时需要遵守所在国家和地区的相关法律法规。 总结来说,在线的pyc,pyo反编译工具可以将编译过的Python二进制文件转化为可读的源代码,用于学习、调试和分析代码。但使用反编译工具需要遵守相关法律法规,并且反编译得到的代码可能存在与源代码不完全一致的情况。 ### 回答3: 在线pyc,pyo反编译是指通过在线工具或网站对Python编译文件(.pyc或.pyo)进行逆向操作,将其还原成可读的Python源代码。反编译可以帮助我们了解编写该文件的人所使用的算法和逻辑,以及修复已编译文件中的错误或进行研究和学习。 在线pyc,pyo反编译工具通常使用解析已编译文件的字节码并将其转换回等效的Python源代码。这些工具会分析字节码中的指令和数据,并尝试还原这些指令和数据的含义,最终生成与原始源代码相似(但可能不完全一样)的代码。但是由于编译文件是通过将源代码编译为字节码生成的,反编译得到的源码通常不会与原始源代码完全一致,因为编译过程会引入额外的优化和转换。 在线pyc,pyo反编译存在一定的限制和局限性。首先,编译文件中的注释和变量名等元信息通常会丢失,因此反编译得到的源代码可能会缺少这些有用的信息。其次,编译过程中可能会进行各种优化,例如常量合并、循环展开和内联函数等,这些优化可能会导致反编译的源代码与原始源代码存在一定的差异和不一致性。 总体来说,在线pyc,pyo反编译是一种有助于理解和研究Python代码的工具,但它并不是万能的,无法完全还原原始源代码的结构和细节。因此,在使用在线反编译工具的过程中,我们应该谨慎对待反编译得到的源代码,并结合其他信息和工具进行分析和研究。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值