谈到python的文件后缀,说眼花缭乱也不为过.来看看你遇到过哪些类型!
.py
如果这个不知道,呵呵…那请出门左拐,你还是充钱那个少年,没有一丝丝改变。接着打游戏去吧…
.pyc
这个后缀应该算是除了python的py代码外,遇到最多的一种文件类型了。虽然python被普遍认为是一种解释性语言,但谁说它就不能被编译后执行呢?python通过compile生成的pyc文件,然后由python的虚拟机执行。相对于py文件来说,编译成pyc本质上和py没有太大区别,只是对于这个模块的加载速度提高了,并没有提高代码的执行速度,通常情况下不用主动去编译pyc文件。那pyc文件存在的意义在哪里?
除了略微提高的模块加载速度外,更多的当然是为了一定意义上的保护源码不被泄露了。
当然为什么说一定意义?因为既然有编译,就毕竟存在反编译喽,这个一会儿说…
如何将py文件编译成pyc文件呢?方法有三:
1.使用python直接编译
python -m sample.py
2. py_compile
import py_compile
py_compile.compile('sample.py')
3. compileall
import compileall
compileall.compile_file('sample.py')
compileall.compile_dir(dirpath)
三种方式一看便知,区别在于compileall可以一次递归编译文件夹下所有的py文件
.pyo
pyo是源文件优化编译后的文件,pyo文件在大小上,一般小于等于pyc文件。这个优化没有多大作用,只是移除了断言。原文如下:
When the Python interpreter is invoked with the -O flag, optimized code is generated and stored in .pyo files. The optimizer currently doesn’t help much; it only removes assert statements. When -O is used, all bytecode is optimized; .pyc files are ignored and .py files are compiled to optimized bytecode.
使用方式:python -O -m py_compile sample.py
.pyd
看到了前几种,相信d这个字母大家猜猜就能想到,它是python的动态链接库;动态链接库(DLL)文件是一种可执行文件,允许程序共享执行特殊任务所必需的代码和其他资源;你在python安装目录的DLL文件夹下,就可以看到它们了。
.pyz(w)
从Python 3.5开始,定义了.pyz和.pyzw分别作为“Python Zip应用”和“Windows下Python Zip应用”的扩展名。
新增了内置zipapp模块来进行简单的管理,可以用Zip打包Python程序到一个可执行.pyz文件。
使用也比较简单,但无法想pyinstaller等打包exe工具一样脱离python环境单独运行,而且这类文件往往拿文本编辑器就能看到它的源码信息,总体来说还是比较鸡肋。
.exe
为什么会再单独提到exe文件,因为python有很多可以将python源码打包成exe的工具,从而脱离python环境单独运行。感兴趣的朋友可以去看看我之前总结的文章:Python打包工具--Pyinstaller详细介绍
python反编译
所谓道高一尺魔高一丈,不管是python、java还是C语言,只要有编译,必然存在反编译的操作。不管再怎么去考虑加密,势必都有人能破解,只是成本不同而已,所以编译只可作为一种防君子不防小人的操作。今天主要和大家聊聊pyc文件的反编译,其中有哪些小心机的做法与陷阱。
首先,反编译既然是一种非正常手段,python自然不会原生附带该模块,我们需要安装它:pip install uncompyle6
操作呢,也比较简单命令行下输入:uncompyle6 sample.pyc(pyo) > sample.py
这就完了?说好的心机与陷阱呢?哈哈,接着看…
使用注释的心机
我们来创建一个名为BreezePython.py的文件,并将下面的代码进行编译和反编译,来看看反编译后的内容与原始代码有什么差异
# -*- coding: utf-8 -*-
# @Author : 王翔
# @微信号 : King_Uranus
# @公众号 : 清风Python
# @GitHub : https://github.com/BreezePython
# @Date : 2019/12/15 11:16
# @Software : PyCharm
# @version :Python 3.7.3
# @File : tmp.py
import platform
def test():
"""
这是一个测试方法,用来验证反编译
:return: None
"""
# 打印系统详情
print(platform.platform())
test()
编译:
python -m BreezePython.py
反编译:
uncompyle6 BreezePython.cpython-37.pyc > output.py
下来看看反编译后的文件有什么区别吧:
# uncompyle6 version 3.6.4
# Python bytecode 3.7 (3394)
# Decompiled from: Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)]
# Embedded file name: C:UsersAdministratorDesktopuncompileBreezePython.py
# Size of source mod 2**32: 453 bytes
import platform
def test():
"""
这是一个测试方法,用来验证反编译
:return: None
"""
print(platform.platform())
test()
# okay decompiling BreezePython.cpython-37.pyc
相比于原始的代码,有什么差别?没错,注释…文档注释默认会保留,但使用# 进行的注释,却在编译+反编译的过程中丢失了。那么如果你是个心机boy,代码注释都用#去写吧,即便反编译了代码,没有注释的情况下,即便我们自己也会被大段没有注释的代码搞疯掉了,哈哈。
反编译的陷阱
反编译就万无一失么?NO…举一个例子吧,这段代码随意而为,只是为了让反编译的时候抛出错误:
class Demo:
def __init__(self, color):
self.color = color
def jduge(self):
if not self.color:
return "get None"
if not self.color == 'red':
mood = "disappointed"
return "I'm %s,I like white best" % mood
return "get None..."
if __name__ == '__main__':
Main = Demo("red")
print(Main.jduge())
正常情况这段代码应该返回:get None…
但这段代码反编译后成了什么样子?
# uncompyle6 version 3.6.4
# Python bytecode 3.7 (3394)
# Decompiled from: Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)]
# Embedded file name: C:UsersAdministratorDesktopuncompileBreezePython.py
# Size of source mod 2**32: 391 bytes
class Demo:
def __init__(self, color):
self.color = color
def jduge(self):
if not self.color:
return 'get None'
else:
mood = self.color == 'red' or 'disappointed'
return "I'm %s,I like white best" % mood
return 'get None...'
if __name__ == '__main__':
Main = Demo('red')
print(Main.jduge())
# okay decompiling __pycache__BreezePython.cpython-37.pyc
编译后的代码返回:I'm True,I like white best
什么鬼?这编译成什么了乱七八糟的了东西了。京剧多年采坑经验,反编译的代码,当遇到多个if not 的时候,最后一个if not就会被所谓的简要写法弄的变了为了,导致最终反编译出错。这个是个坑,同样你是否也可以利用它呢?
今天的文章就到这里,希望通过这篇文章能让你对python的文件类型与编译、反编译操作有所了解。
作者:华为云特约供稿开发者 清风Python