最近有个项目,需在 Windows 中调用 Python,执行脚本功能。功能已完成,但接下来就是 Python 环境部署的问题。主要有2种选择:
程序安装包不自带 Python 环境,由用户在自己机子上安装Python后,再将其配置在程序的 Python 路径中;
程序安装包自带 Python 环境,用户机子可不用安装;
第一种好处是程序会小些,但对用户而言不友好;第二种则程序变大,但比较完整,对用户友好;若增加的大小在可接受范围内,则果断采取第二种。
1. embeddable 包探索
经过种种探索,Python官网提供了embeddable版本,为最小可运行环境,解压完只有 12.9 M,完美适应第二种的需求。
进一步研究,embeddable 版本是将 python 运行所需要的库全部编译为 .pyc 文件,并且把一些常用的库压缩入一个 zip 文件里,这样做有2点好处:
提升了性能:pyc 为编编译过的字节码,可直接由解释器运行;
减少了大小:zip 压缩;
若只是简单地提供了 python 运行环境,至此功能已完全满足。
然而,python 的强大与流行,还在于有大量强大的第三方包,而 embeddable 中不包含其它第三方包,也不包含 pip 等 package 管理工具。
2. embeddable 包扩展
经研究,embeddable 可进一步扩展。如下:
增加 lib/site-packages 文件夹,同完整安装包一样,用于放第三方包,可在此放入 pip 等需要的 package;
修改 python36._pth,加入 "./Lib/site-packages" ,如此 python 解释器就能找到我们添加的 lib/site-packages 包了;
python36._pth
研究 python36._pth 可知,embeddable 版本的 python 解释器是读取这里路径,因此也可以自定义 zip 包后,在此加入路径配置。
3. site-packages 预编译(可选)
一般而言,site-packages的包一般是源码,若想进一步提高性能,可预先将 .py 编译为 .pyc 文件,步骤如下(由 python 脚本来处理):
编译 /lib 里的所有文件
import compileall
compileall.compile_dir('Lib/',force=True)
编译完成后,删除 .py 的源文件;
import os
import shutil
def delete_file(folder):
for file in os.listdir(folder):
file_path=os.path.join(folder,file)
try:
if file.endswith('.py'):
if os.path.isfile(file_path):
os.unlink(file_path)
if os.path.isdir(file_path):
delete_file(file_path)
except Exception as e:
print(e)
delete_file('Lib')
编译完的 .pyc 文件在 __pycache__ 文件夹,并且文件名带 .cpython-36.pyc 的后缀,需将其拷贝至 __pycache__ 外,并去除后缀中的 .cpython-36;
import os
import shutil
from pathlib import Path
def copy_cache_files(folder):
for file_name in os.listdir(folder):
file_path=os.path.join(folder,file_name)
print(file_name)
print(file_path)
try:
if os.path.isdir(file_path):
if file_name==('__pycache__'):
parent_dir=Path(file_path).parent
print(parent_dir)
files=os.listdir(file_path)
for pyc_file in files:
print(pyc_file)
source_file=os.path.join(file_path,pyc_file)
dest_file=os.path.join(parent_dir,pyc_file)
if dest_file.endswith('cpython-36.pyc'):
dest_file=dest_file.replace('cpython-36.pyc','pyc')
print(source_file)
print(dest_file)
shutil.move(source_file,dest_file)
os.rmdir(file_path)
else:
copy_cache_files(file_path)
except Exception as e:
print(e)
copy_cache_files('Lib')
删除 __pycache__ 文件夹;
import os
import shutil
def delete_cache(folder):
for file in os.listdir(folder):
file_path=os.path.join(folder,file)
try:
if file=='__pycache__':
shutil.rmtree(file_path)
elif os.path.isdir(file_path):
delete_cache(file_path)
except Exception as e:
print(e)
delete_cache('Lib')
可先这些脚本保存为不同的 .py 文件,最好注明先后运行的脚本,存放于 embeddable 的目录下。启动 embeddable 的脚本解释器 python.exe ,按步骤输入对应的脚本文件,回车运行即可。为保险起见,可每运行一步,查看 site-packages 的变化。
当然,也可以不预先编译 .pyc ,因为 .pyc 文件会比 .py 大,且python 解释器首次运行,会自动将 .py 编译为 .pyc 文件。
4. 总结
综上,总结一下:
使用 python 官网的 embeddable 版本;
若需要,可新增 zip 包,修改 python36._pth 配置;
若需要,可增加 Lib/site-packages 文件夹,修改 python36._pth 配置;
若需要,可把 Lib/site-packages 里的 .py 文件预先编译为 .pyc 文件;