最近用python写了一个小程序,想发布出去让人试用又不想暴露源码,搜索了一下发现将py文件编译成pyd文件就能达到目的。
转换过程很简单,但是在调用pyd文件并且打包为单个exe文件的时候遇到一个坑,搞了一天才解决,在这里分享一下。
首先安装cython库
个人比较喜欢用清华的镜像库,速度快。
- pip install Cyphton -i https://pypi.tuna.tsinghua.edu.cn/simple
然后创建一个setup.py文件
写入以下内容:
- from distutils.core import setup
- from Cython.Build import cythonize
- setup(ext_modules=cythonize("BetaV14.py"))
BetaV14.py就是要转换为pyd文件的代码文件
命令行输入:
- python setup.py build_ext --inplace
会在.py文件目录下生成一个BetaV14.cp37-win_amd64.pyd文件,文件名中“.cp37-win_amd64”这一段可以删除,不删除也可以正常调用;但原文件名字段不能改变。
接下来需要打包发布为.exe文件
我用的是pyinstaller,还是用清华镜像库安装。
- pip install pyinstaller -i https://pypi.tuna.tsinghua.edu.cn/simple
根据一些教程,有的说在命令行直接输入:
- pyinstaller -F BetaV14.py
就能直接引用pyd文件打包发布exe文件,但是在我这里出现文件缺失的错误:
ValueError: Module file F:\python项目1\BetaV14.py is missing
继续查找问题,发现需要用一个入口程序来导入pyd文件,于是创建一个main.py文件,import刚才生成的模块,pyd文件默认优先级高于py文件,可以在后面解包exe文件来验证。
- import BetaV14
- if __name__ == '__main__':
- BetaV14()
这里需要注意的是程序的__main__入口只能有一个,如果源py文件中有定义main入口,需要注释掉并调整代码缩进,否则通过main.py调用pyd文件遇到if name == ‘main':之后的代码都不会运行。
接着命令行输入:
- pyinstaller -F main.py
打包成.exe文件,在dist目录下发现main.exe文件大小只有5M,之前采用py文件打包的程序有接近50M,运行之后闪退。这个问题想了半天才想出来,可能是引用了大量的第三方库没有打包进去,于是将源py文件头部import部分全部复制到main.py文件头部。
- import win32gui
- import win32api
- import win32con
- import time
- import random
- import datetime
- import os,sys
- import configparser
- import numpy as np
- from PIL import Image
- from scipy.signal import convolve2d
- import http.client
- import subprocess
- import BetaV14
- if __name__ == '__main__':
- BetaV14()
再次用命令pyinstaller -F main.py打包,得到正常大小的.exe文件,点击能正常运行。
接下来我们用pyinstxtractor.py(不清楚该脚本是否涉及著作权,请自行搜索)解包exe文件验证一下,命令行输入:
- python pyinstxtractor.py main.exe
会得到一个main.exe_extracted文件夹,在文件夹下发现文件BetaV14.pyd,说明通过引用pyd文件打包成功。
在此作为一个初学者记录一下自己遇到的坑,让大佬们见笑了。
补充:python打包编译成pyd或者_python之setup.py的那些事
今天偶然对setup.py产生了兴趣,以前只知道可以用它来安装包,例如
python setup.py build ->python setup.py install.当然前提你下载的这个源码包是压缩的,之前对这个理解并不深,今天偶然看见pip install -e . 的用法,然后串起来想了一下。
我的目录结构如上,首先我创建了一个setuptutorial的directory,然后我在下面创建了greet_pkg的python package,并且在setuptutorial下面创建了setup.py如下
- from setuptools import setup, find_packages
- setup(
- name='greet',
- version='1.0.0',
- packages=find_packages(include=['greet_pkg', 'greet_pkg.*']),
- url='',
- license='uestc',
- author='jack',
- author_email='2444093230@qq.com',
- description='test package',
- py_modules=['greet2'],
- install_requires=['pyjokes']
- )
greet2.py如下
- def greet2(name):
- print(
- 'hello',name,'this is greet2'
- )
在greet_pkg下面下了一个greet.py如下
- import pyjokes
- def greet(name):
- print('hello!', name, f'im telling you a joke {pyjokes.get_joke()}')
整体目录结构和setup.py就如上所示
接下来好戏开场了,如果我要在任意其他文件里面使用到我定义的greet()方法,以前的做法是按照import规则在其他文件里面导入,当然如果写的不规范,及其的容易出问题,这里我提供另外一个思路,在setuptutorial下面使用pip install -e . 命令,将setup.py里面包含的package和py_module安装到Libary root下,当然他的实际的location不是在Libary root下,这个你可以在pip install -e . 之后使用pip show greet 查看他的信息.
到这里就完了吗?
当然没有,这个就是之前的python setup.py build 的作用,我这里猜测大概率是把tar.gz包转化成我上述的目录结构一样的directory。
而python setup.py install 的作用就类似于pip install '-e' . 。而且python setup.py install 之后的greet包是真的存在于sitepackages里面的。
setup.py除了上述安装包的作用,还可以是他的逆过程如 python setup.py sdist 成greet.tar.gz,这样就有上述的装包的过程先build再install。
还可以使用setup.py将py文件转化为pyd,也可以说将pyx文件转化为pyd,
- from setuptools import setup
- # from distutils.core import setup
- from Cython.Build import cythonize
- # setup(
- # name='hello',
- # ext_modules=cythonize(['sayhi.py'])
- # )
然后运行python setup.py build_ext --inplace就可以了!
pyd文件可以很好的隐蔽py文件里面的实现,和linux下的so文件类似。
有类似py->pyd功能的有easycython模块,可以直接pip安装。
有人可能会说pyc也看不见源码吗?
但是他可以被反编译23333
至于如何将py编译成pyc或pyo
可以使用py_compile或者compileall,不了解的可以自行搜索一下,都有很多的例子,还有针对pyc的反编译库,都可以搜到,至此setup.py我所了解的功能都谈完了,里面还有很多参数可以灵活配置,实现更加炫酷的效果可以查看这个链接setup.py
原文链接:https://blog.csdn.net/weixin_45116696/article/details/104861915