python文件编译_我算是白学Python了,现在才知道原来Python是可以编译的

u=1761236581,1065860599&fm=173&app=49&f=JPEG?w=600&h=372&s=3B82AC4E8AA0AC531C3EC4830300608B斌哥说

大家好,我是斌哥。

一说起Python,可能开发者第一时间想到的就是:“Python是一门能快速开发的解释型语言”。

没错,Python确实是一门解释型的语言,而对比Java这种编译后再解释型的语言似乎就显得不够友好了。其实不然,Python一样可以编译。

Python和Java的解释方式对比

Java:源代码 -> 编译成class -> Jvm解释运行

Python:源代码 -> Python解释器解释运行

我经常和身边的Java开发者开玩笑说:“Java真变态,别的语言都是要么直接编译要么直接解释,Java太另类了又编译又解释的......”

直接解释和编译后解释其实最大的区别就是源代码的安全性。

如果是直接解释源代码,那么源代码没有安全性可言,也就是任何一个人都可以打开源代码一看究竟,任何人都可以随意修改源代码。

事实上,Python和Java的解释方式是相同的,只是我们表面上看Python是直接解释源代码,而实际上python解释器只会加载字节码。细心的小伙伴肯定发现了这一点,当我们import某个模块时,总是会在模块所在的目录创建一个__pycache__目录,里面存放着被加载模块的字节码文件。

编译源代码有以下作用:

源代码保护(算法保护)/ 防止用户篡改源代码解释器加载代码速度加快Python的几种文件类型

Python有以下几种类型的文件:

py:Python控制台程序的源代码文件

pyw:Python带用户界面的源代码文件

pyx:Python包源文件

pyc:Python字节码文件

pyo:Python优化后的字节码文件

pyd:Python的库文件(Python版DLL)、在Linux上是so文件

pyc和pyo的生成方法

pyc的作用是用来跨平台使用的,和Java中的Class文件类似。pyc文件是一种字节码文件,可以加快Python解释器的加载速度,当然也可以用来做简单的防源码泄露保护。

pyo则是优化过后的字节码文件,不过pyo更像编译型语言里的中间文件。

我们可以通过Python提供的py_compile模块来进行源代码的编译。

py_compile模块只提供3个方法,分别是有关编译异常PyCompileError、有关编译compile、有关程序入口main

我们要用到的是compile方法,compile原形如下:

compile方法原形

compile(file, cfile=None, dfile=None, doraise=False, optimize=-1)

有5个参数:

file:必选参数,要编译的源文件

cfile:编译后的文件,默认在源文件目录下的__pycache__/源文件名.解释器类型-python版本.字节码类型

###例如:__pycache__/abc.cpython-34.pyo

dfile:错误消息文件,默认和cfile一样

doraise:是否开启异常处理,默认False

optimize:优化字节码级别

这里分为4个等级,文档中是这样写的:

u=1144445523,4229223859&fm=173&app=49&f=JPEG?w=581&h=267&s=E982E51A818DD8EA046D10D0030010B2optimize级别

param optimize: The optimization level for the compiler. Valid values

are -1, 0, 1 and 2. A value of -1 means to use the optimization

level of the current interpreter, as given by -O command line options.

optimize为1时,优化字节码级别为最高

-1和0:设置pyc优化级别

1和2:设置pyo优化级别

数字越小,优化级别越高

准备源文件a.py和b.py,内容相同,就是一句print("python")代码

编写编译脚本:

import py_compile

py_compile.compile(file = "a.py",cfile = "a.pyc",optimize=-1)

py_compile.compile(file = "b.py",cfile = "b.pyo",optimize=1)

u=305223156,3031861015&fm=173&app=49&f=JPEG?w=526&h=87编译脚本代码

运行后可以看到已经成功编译成字节码文件了,分别为a.pyc和b.pyo

用文本编辑器打开a.pyc和b.pyo,如图

u=993683268,3301849581&fm=173&app=49&f=JPEG?w=640&h=96打开字节码文件

尝试运行这2个字节码文件。

u=1101235309,3084153891&fm=173&app=49&f=JPEG?w=640&h=418&s=8071CB2013AEB74D5EDD110B020070C1运行效果

可见,字节码文件成功运行。

也可以直接通过Python加载模块来运行:

#编译成pyc

python -m py_compile 源代码

#编译成pyo

python -O -m py_compile 源代码

这确实可以简单地保护我们的代码,同时似乎看起来像是加密的效果,但是要注意,这不是加密,只是把源码变成优化后的字节码而已,如果想要获得源码,我们一样可以通过逆向编译来得到源码,目前有专门逆向Python字节码的工具存在。

如果需要编译整个目录内的所有源代码,请参考Python compileall

pyd可以让我们的代码更安全

如果真的想要保护代码,为何不考虑把它变成python扩展模块?(目前还没有pyd被反编译的消息)

pyd是Python中的扩展模块,相当于windows的dll,不同的是pyd只供python调用而已。

实际上,大部分的包、小模块都是以pyd形式发布的。

如果特别感兴趣的小伙伴可以深入研究下setuptools和distutils

在把源代码转换成功pyd之前,我们需要用到Cython包。

pip list | findstr "Cython"

u=2458253890,2658247651&fm=173&app=49&f=JPEG?w=488&h=134&s=E553CB2069EB2C015A79B8030000E0C1检查Cython包

检查是否安装了Cython,没有请pip install Cython安装即可

编译pyd步骤1:生成C代码

import Cython.Build

#导入Build模块

Cython.Build.cythonize("a.py")

#a.py转换成C代码

cythonize运行完成之后,无异常的情况下会在a.py的目录下创建一个a.c文件,同时会返回一个distutils.extension.Extension对象列表

一定要注意的是:如果在Python Shell测试,一定要用绝对路径,否则会ValueError异常,cythonize不会从sys.path中读路径。

u=3370181628,3955241486&fm=173&app=49&f=JPEG?w=418&h=92&s=4C00A51AC5266D20164DD4DA0100D0B3生成C代码

编译pyd步骤2:利用distutils生成pyd扩展模块

此时我们可以用distutils包来编译成我们要的pyd模块

编译a.py成pyd

import Cython.Build

import distutils.core

a = Cython.Build.cythonize("a.py")

#返回distutils.extension.Extension对象列表

distutils.core.setup(

name = 'pyd的编译',#包名称

version = "1.0",#包版本号

ext_modules= a,#扩展模块

author = "百家号——斌哥说Python",#作者

author_email='binnlzeng@163.com'#作者邮箱

)

u=2160941831,257269057&fm=173&app=49&f=JPEG?w=599&h=271&s=CC02E41B1B186C434A4998D20000C0B3代码

python 执行编译的脚本 build

python 执行编译的脚本 build_ext

u=923878009,570454558&fm=173&app=49&f=JPEG?w=640&h=418&s=2470EB2213BEBFCC5C703C0B0000E0C1执行编译脚本编译

此时会在编译脚本所在目录生成一个build目录,里面存着C语言的.def文件和.o文件,还有我们要的pyd文件

u=2483646983,3730784371&fm=173&app=49&f=JPEG?w=602&h=61生成的C文件

u=1572334663,3361519039&fm=173&app=49&f=JPEG?w=600&h=44生成的pyd文件

批量编译pyd文件的误区

此时我们已经生成了1个pyd文件,如果我们是扩展包/模块的开发者,怎么批量编译呢?

总有人会犯错,例如以下2个例子:

a = Cython.Build.cythonize("a.py")

b = Cython.Build.cythonize("b.py")

distutils.core.setup(

...,

ext_modules= [a,b]

)

这样做吗?NO......

a = Cython.Build.cythonize("a.py")

a.append(Cython.Build.cythonize("b.py"))

distutils.core.setup(

...,

ext_modules= a

)

还是这样?

犯这样的错原因却是因为:

a = Cython.Build.cythonize("a.py")

type(a)

提示

没错,Cython.Build.cythonize返回的是一个列表,里面有只有1个distutils.extension.Extension对象

报错如下:

u=1630869122,1053934329&fm=173&app=49&f=JPEG?w=640&h=119&s=40708262CD44BF700ED924040000E0C2异常信息

需要1个Extension或者是2个元组

批量编译pyd

方法1:提取我们要的Extension对象

import Cython.Build

import distutils.core

a = Cython.Build.cythonize("a.py")[0] #提取Extension对象

b = Cython.Build.cythonize("b.py")[0]

distutils.core.setup(

name = 'pyd的编译', #包名称

version = "1.0", #包版本号

ext_modules= [a,b], #被扩展的模块

author = "百家号——斌哥说Python", #作者

author_email='binnlzeng@163.com' #作者邮箱

)

方法2:转换成C代码后再进行Extension对象实例化

import Cython.Build

import distutils.core

Cython.Build.cythonize("a.py")

Cython.Build.cythonize("b.py")

distutils.core.setup(

name = 'pyd的编译', #包名称

version = "1.0", #包版本号

ext_modules= [distutils.core.Extension('a',["a.c"]),distutils.core.Extension('b', ['b.c'])], #被扩展的模块

#[

#distutils.core.Extension('a',["a.c"]),

#distutils.core.Extension('b', ['b.c'])

#]

author = "百家号——斌哥说Python", #作者

author_email='binnlzeng@163.com' #作者邮箱

)

u=1847221970,3710414160&fm=173&app=49&f=JPEG?w=468&h=159&s=CD12E51A85B84420145490D9000080B3调用pyd运行结果

pyc和pyo相对而言安全性较低,pyd是目前解决Python开发中代码安全性最优的一个方案。

但是要注意一点:无论是pyc还是pyo、pyd,都是跟着Python版本走的,不要指望Python2.7的东西在Python3上完美运行。

PS:如果遇到running build...提示,删掉build目录重新编译即可。

我是斌哥,喜欢请点击关注。

斌哥说Python,只专注于Python技术!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值