python打包含有参数传递的exe程序
参考链接:json解析Python中json模块的load/loads方法实战及参数详解记住一点,load方法的作用对象是文件流(with open之后的f句柄),loads方法的作用对象是字符串。参数传递常见的就是 sys.argv还有argparse(这个库不是内置的,需要pip install argparse),前者功能比较简单,能提供的有限,后者虽然不是内置的,需要安装,但是确实功能比较
文章目录
1. json解析
记住一点,load方法的作用对象是文件流(with open之后的f句柄),loads方法的作用对象是字符串。
2. 参数传递
常见的就是 sys.argv还有argparse(这个库不是内置的,需要pip install argparse
),前者功能比较简单,能提供的有限,后者虽然不是内置的,需要安装,但是确实功能比较强大。
所以在开发时选择后者(如果最后打包程序或者在用户环境运行有问题,那就考虑换掉,使用前者,这是后话)。
大致参数代码demo:
# 只有三个参数 id(str) 实验类别序号 continue_transfor(str) 是否继续传递 data(json) 实验数据
import argparse
parser = argparse.ArgumentParser(
description='Writing the input json data to the corresponding .xls template file and save ')
# 这里的描述会出现在 usage下方 表明这个程序的作用是什么
parser.add_argument("--id", type=str, default="01")
parser.add_argument("--continue_transfor", type=int, default=0)
parser.add_argument("--data", type=str, default="")
args = parser.parse_args()
上述代码在命令行运行的效果:
简单的代码说明(参考Argparse -官方文档(可以选语言的)):
- 使用 argparse 的第一步是创建一个
ArgumentParser
对象,ArgumentParser 对象包含将命令行解析成 Python 数据类型所需的全部信息- description参数:在参数帮助文档之前显示的文本
- 给一个 ArgumentParser 添加程序参数信息是通过调用
add_argument()
方法完成的。通常,这些调用指定 ArgumentParser 如何获取命令行字符串并将其转换为对象。这些信息在 parse_args() 调用时被存储和使用。 - ArgumentParser 通过
parse_args()
方法解析参数。它将检查命令行,把每个参数转换为适当的类型然后调用相应的操作。在大多数情况下,这意味着一个简单的 Namespace 对象将从命令行解析出的属性构建.在脚本中,通常 parse_args() 会被不带参数调用,而 ArgumentParser 将自动从 sys.argv 中确定命令行参数
最终的效果:
parser = argparse.ArgumentParser(prog='WriteJSON',
description='Writing the input json data to the corresponding .xls template file and save')
parser.add_argument("-i", "--id", type=str, default="01", help='experiment type number')
parser.add_argument("-c", "--continue_transfer", type=int, default=0,
help="whether to continue to transfer data. value=0 don't transfer, else continue transfer")
parser.add_argument("-d", "--data", type=str, default=None, help='data with json format')
parser.add_argument('--version', action='version', version='%(prog)s 1.0')
args = parser.parse_args()
id = args.id # 不给也可以 默认01
continue_transfer = args.continue_transfer # 不给也可以 默认0
data = args.data
print('id', id)
可以看到有个比较奇怪的地方是所有的参数后面都会跟上一个同名参数的大写形式,根据argparse.html#metavar可知,只需要在每个参数里设置metavar='' metavar等于空字符串
那个大写就会消失了(暂时不清楚会有什么负面影响)
(PS:version参数里不用加这个 不然报错)
- 运行python脚本时传入参数的几种方式
- python中的argparse模块使用
- python argparse使用
- Argparse -官方文档(可以选语言的)
- https://wiki.jikexueyuan.com/project/explore-python/Standard-Modules/argparse.html
3. 动态调用函数
直接上例子:
# 比如你有6个函数,属于同一类型的函数,比如有6种实验用的表格,你要根据实验序号去调用相应的修改该表格的函数
def experiment01():
pass
def experiment02():
pass
....
类似这样的6个函数,每次你可以获取实验序号:比如02,希望可以根据传递的实验序号序号来动态调用相应的函数
eval('experiment'+id)()
# 这样就可以了
参考链接:
4. 配合Pycharm使用argparse
JSON在线:在线处理json的网站,格式化/压缩
编辑配置,
如果要传递的参数中 含有特殊符号(比如json格式数据中的 双引号),则需要进行转义,才可以传入后续的逻辑处理函数中进行处理。
在pycharm中配置的时候,应该类似(参数名称 空格 引号包住要传递的参数):
--data
'{"tableData_vol":[{"id":"7c4f44d0-bed1-41e5-b47f-c394080b5d8b","freq":0,"range":"100mV","actualVal":"20mV","detectVal":["20mV","/"]},{"id":"d2edb143-ac21-432d-b7bf-01a1641ba306","freq":"400Hz","range":"1V","actualVal":"30mV","detectVal":["20mV","/"]}]}'
在真正将这个参数值传入处理逻辑前,要使用str.replace,将所有的"
替换为\"
。无效,因为发现但凡是使用了命令行参数读入,pycharm处理传入的参数时,就已经把所有的引号去掉了,只能在获取参数后再程序中再自行加入引号。。。
使用转义符也白搭,没啥用(replace失败的原因不是因为转义符,是因为程序中获取的json数据中已经没有引号了,被命令行参数去完了)
想了想,获取之后处理,完全没有传递前处理方便,沟通最重要,丢出去了
参考:
5. 关于路径
os.getcwd()
# 这个函数不一定返回当前工作目录,
# 参考上面的博客,获取当前脚本的绝对路径,然后再去获取父目录,再进行操作
import os
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
# os.path.abspath(__file__) 获取当前脚本的绝对路径
output_file = open(os.path.join(BASE_DIR, 'file_name'), 'wb')
# do something
output_file.close()
5.1 关于pyinstaller打包后的路径问题
一开始使用的是绝对路径,放到另一个电脑上也可以使用,命令行调用exe文件是OK的,但是同事使用Nodejs来调用执行exe,同时她也要对自己的程序进行打包发布,因此路径问题就更麻烦了(和同事联调的时候比较困难)。故参考以下:(不再使用绝对路径,而是使用冻结路径的方式)
如果python 文件打包成了exe文件,那么sys.executable将显示为被执行exe路径,此外sys会多出frozen属性
参考
6. 打包成exe
首先要大致了解一下Pyinstaller打包.py的原理,
根据PyInstaller Manual,可知 PyInstaller将Python应用程序及其所有依赖项捆绑到一个包中。 用户无需安装Python解释器或任何模块即可运行打包的应用程序。 PyInstaller支持Python 3.5或更高版本,并正确捆绑了主要的Python软件包,例如numpy,PyQt,Django,wxPython等。
打包完之后运行XX.exe 可能会出现一闪而过的情况(程序执行完就结束了,根本看不清控制台输出了什么),参考解决pyinstaller打包发布后的exe文件打开控制台闪退的问题,直接切到xx.exe程序存在的目录,使用命令行进行运行,就可以咧。
例如:
打包很简单,直接切到要打包的入口py文件所在的文件夹,命令行输入:
pyinstall xxx.py -F(打包后只生成单个exe格式文件) -w(不使用控制台) -i=xxx.ico(修改生成exe的图标)
- -c 使用控制台 默认
然后就可以在dist文件夹里看到生成的 exe文件了
6.1 简单的加速
pyinstall xxx.py -F(打包后只生成单个exe格式文件) -w(不使用控制台) -i=xxx.ico(修改生成exe的图标)
采用上面这种 -F参数之后,打包得到的exe差不多10MB,但是执行速度奇慢。。。
参考:pyinstaller生成的小程序exe启动花费4秒,能有啥办法优化编译一下吗?
- 去掉-F参数(这个参数会将生成的文件压缩全都打包进exe中,而执行的时候其实还是要解压到一个临时目录中,所以会导致程序运行速度变慢)
- 加入-w参数 不使用控制台(执行时不会弹出控制台的黑框(不过这样一些print信息就输不出来了,不利于调试))
加入-F和不加的区别,如下图
运行后者,速度非常快,基本和运行.py文件速度一样
6.2 原理解释
官方解释:翻译(来自:stack overflow-Speeding up an .exe created with Pyinstaller中一个回答帖的链接:)
- 官方文档—What PyInstaller Does and How It Does It,关于pyinstaller如何打包成一个.exe并运行的(简单来说:需要提取程序的完整环境并将其写入临时文件夹。)
原文翻译:结合谷歌翻译自己看就行了,需要注意的几点:
-
使用Pyinstaller打包得到的.exe文件是特定于具体的操作系统和特定的python版本的,即如果要为以下的环境准备发布版本:
- 不同的操作系统
- 不同的Python版本
- 32/64位不同
则可以在那个python版本,那个操作系统下运行Pyinstaller。然后执行Pyinstaller的Python编译器是bundle(包)的一部分,它会特定于具体的操作系统和特定的字长。
-
打包成一个文件夹,其优点(2个)和工作方式如下:
- 传输方便(压缩,解压到另一个电脑,双击exe文件就可以运行),而且debug方便,使用这一方式时,很容易调试在打包exe时出现的问题。可以确切地看到PyInstaller收集了哪些文件到该文件夹中。
- 另一个有点是如果代码发生了改变(不涉及依赖包的改变的话),只需要发给别人更新过的.exe文件就可以,这比更新整个文件夹要方便的多。(反之,如果脚本引入了新的依赖包或者更改变了依赖包,就需要重新分发这一整个文件夹)
- 工作方式:打包的文件始终在Pyinstaller的bootloader(根引导)中开始执行,这是打包的文件夹中可执行文件的核心。 Pyinstaller bootloader是活动平台(windows GNU/linux Mac OS等)上的一个二进制可执行程序,当用户启动程序时,其实是bootloader在运行。bootloader 创建了一个临时的Python虚拟环境,这样Python编译器(interpreter)就可以在这个脚本文件夹中找到所有引入的模块/库。bootloader启动了一个Python编译器的副本来执行脚本,后续的执行正常都是从这里开始的,提供被包括在脚本文件夹中所有支持的文件。
大概就是这样,如果想看具体的细节,参见The Bootstrap Process in Detail
-
打包成一个单独的文件,优缺点及工作方式如下:
- PyInstaller可以把所有的脚本及其依赖包都打包到一个.exe文件中,优点就是看不懂文件夹中其他内容的用户可以只得到一个可以理解是啥的.exe文件,缺点是每次更新都要重新发布全部内容(一个.exe 不过体积会大一些),同时,单个文件比单个文件夹启动要慢
- 在单文件模式下,bootloader也依然是核心。启动时,会在当前操作系统的合适的临时文件夹就下创建一个临时的文件夹,这个文件夹的名字类似于
_MEIxxxxxx
,其中xxxxxx
是一串随机生成的数字。 这个可执行文件包括:你的脚本所使用的的所有python模块的一个嵌入式存档,同样还有所有非Python支持的文件(例如:.so
文件)压缩的副本。bootloader 会解压这些文件并将其写入临时文件夹中,这会花费点时间,这就是为什么单个exe方式会比单个文件夹方式启动慢的原因。
6.3 其他打包库
搜索过程中,还发现了一个叫nuitka
参考:nuitka打包的exe比用pyinstaller打包的exe运行速度快?
还有个叫cx_freeze
参考:Executable generated with pyinstaller is slower than cx_freeze
7.pyinstaller的其他问题
7.1 双击exe 不弹出黑框
参考Stack Overflow回答:
Getting rid of console output when freezing Python programs using Pyinstaller
方案1
加入一个--noconsole
参数,使用以下代码:
pyinstaller --noconsole XXX.py
参数说明来自pyinstaller文档
方案2
先用pyinstaller打包一次,然后在产生的.spec文件(和pyinstaller -F xx.py
的.py文件同名),打开
将这里的True
改成False
,然后运行:
pyinstaller your_script.spec
参考链接
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)