当前位置: 首页 > news >正文

用PyInstaller构建动态脚本执行器:嵌入式Python解释器与模块打包 - 简明教程

技术场景:

需分发的Python工具要求终端用户可动态修改执行逻辑将Python环境与指定库(如NumPy/Pandas)嵌入可执行文件实现"一次打包,动态扩展"的轻量化解决方案。

▌ 架构设计原理

1. 双模运行时识别

# 核心判断逻辑(适配开发模式与编译模式)  
def get_runtime_path():  
    if getattr(sys, 'frozen', False):  
        return os.path.dirname(sys.executable)   # 编译模式取执行文件路径  
    return os.path.dirname(__file__)             # 开发模式取脚本路径  

设计优势:

  • 开发阶段与发布版本使用同一套代码
  • 避免硬编码路径引发的跨平台兼容问题

2. 动态脚本加载机制

def load_script(script_name):  
    target_path = os.path.join(get_runtime_path(),  script_name)  
    if os.path.exists(target_path):   
        with open(target_path, 'r', encoding='utf-8') as f:  
            exec(f.read(),  globals())  # 在全局命名空间执行脚本  

3. 架构图

在这里插入图片描述

▌ 基础功能实现

1. 编写main.py和Run.py文件(名称随意)

main.py

import os
import sys

# === 预先导入之后可能会用到的模块 ===
import numpy
import pandas


# 核心判断逻辑(适配开发模式与编译模式)  
def get_runtime_path():  
    if getattr(sys, 'frozen', False):  
        return os.path.dirname(sys.executable)   # 编译模式取执行文件路径  
    return os.path.dirname(__file__)             # 开发模式取脚本路径

# 这里一定要定义一个main函数
def main():
    
    # 导入一个可以被修改的脚本代码“Run.py”,后续可以通过它指定要被运行的脚本
    current_path = get_runtime_path()
    ScriptPath = os.path.join(current_path, "Run.py")
    if os.path.isfile(ScriptPath):
        with open(ScriptPath, 'r', encoding='utf-8') as file:
            exec(file.read(),globals())

# 程序入口
if __name__ == "__main__":
    main()

Run.py

import os
import sys


# 核心判断逻辑(适配开发模式与编译模式)  
def get_runtime_path():  
    if getattr(sys, 'frozen', False):  
        return os.path.dirname(sys.executable)   # 编译模式取执行文件路径  
    return os.path.dirname(__file__)             # 开发模式取脚本路径

def load_script(script_name):  
    target_path = os.path.join(get_runtime_path(),  script_name)  
    if os.path.exists(target_path):   
        with open(target_path, 'r', encoding='utf-8') as f:  
            exec(f.read(),  globals())  # 在全局命名空间执行脚本  

# def main():

script_name = "example_1.py"
load_script(script_name)

script_name = "example_2.py"
load_script(script_name)


# if __name__ == "__main__":
#     main()

由于实际运行时上方代码Run.py由main.exe调用,将直接和main.exe共用全局命名空间中的os和sys库,因此

2. 编译 main.py

pyinstaller --onedir main.py

若没有安装pyinstaller,请先使用pip安装:

pip install pyinstaller

编译完的文件如下:

your_project_folder/
│
├──── build/ → 没有用,可以删除
├──── dist/ → 打包后的程序文件夹
│        └─── main/
│            ├─── _internal/ → 需要用到的库文件会被放在这里
│            └─── main.exe → 打包后的可执行程序
│
└──── main.spec → 打包配置文件,可以修改它来定制打包过程

_internal/ 和 main.exe → 可以一起拿出来放在其它位置(需放在同一路径下)

_internal/ 和 main.exe需在同一路径下
_internal/中将包含被引用的numpy和pandas库


3. 测试运行效果

运行 main.exe

test_main.bat

.\main.exe
pause

运行 test_main.bat 文件,查看效果:

D:\test>.\main.exe
This script is running as a compiled executable.
This script is running as a compiled executable.
一维数组:
[1 2 3 4 5]
一维数组加10:
[11 12 13 14 15]
一维数组乘2:
[ 2  4  6  8 10]
二维数组:
[[1 2]
 [3 4]]
矩阵乘法结果:
[[ 7 10]
 [15 22]]
原始DataFrame:
      Name  Age         City
0    Alice   24     New York
1      Bob   27  Los Angeles
2  Charlie   22      Chicago
3    David   32      Houston

年龄大于25的行:
    Name  Age         City
1    Bob   27  Los Angeles
3  David   32      Houston

按年龄升序排序后的DataFrame:
      Name  Age         City
2  Charlie   22      Chicago
0    Alice   24     New York
1      Bob   27  Los Angeles
3    David   32      Houston

描述性统计量:
             Age
count   4.000000
mean   26.250000
std     4.349329
min    22.000000
25%    23.500000
50%    25.500000
75%    28.250000
max    32.000000

D:\test>pause
请按任意键继续. . .


▌ 例程(附)

example_1.py

import numpy as np

# 创建一个一维数组
arr1 = np.array([1, 2, 3, 4, 5])

# 创建一个二维数组(矩阵)
arr2 = np.array([[1, 2], [3, 4]])

# 数组加法
arr_sum = arr1 + 10

# 数组乘法(元素级)
arr_prod = arr1 * 2

# 矩阵乘法
mat_prod = np.dot(arr2, arr2)

# 打印结果
print("一维数组:")
print(arr1)
print("一维数组加10:")
print(arr_sum)
print("一维数组乘2:")
print(arr_prod)
print("二维数组:")
print(arr2)
print("矩阵乘法结果:")
print(mat_prod)

运行结果:(example_1.py)

一维数组:
[1 2 3 4 5]
一维数组加10:
[11 12 13 14 15]
一维数组乘2:
[ 2  4  6  8 10]
二维数组:
[[1 2]
 [3 4]]
矩阵乘法结果:
[[ 7 10]
 [15 22]]

example_2.py

import pandas as pd

# 创建一个简单的DataFrame
data = {
    'Name': ['Alice', 'Bob', 'Charlie', 'David'],
    'Age': [24, 27, 22, 32],
    'City': ['New York', 'Los Angeles', 'Chicago', 'Houston']
}
df = pd.DataFrame(data)

# 显示DataFrame
print("原始DataFrame:")
print(df)

# 数据筛选:选择年龄大于25的行
filtered_df = df[df['Age'] > 25]
print("\n年龄大于25的行:")
print(filtered_df)

# 数据排序:按年龄升序排序
sorted_df = df.sort_values(by='Age')
print("\n按年龄升序排序后的DataFrame:")
print(sorted_df)

# 计算描述性统计量
stats = df.describe()
print("\n描述性统计量:")
print(stats)

运行结果:(example_2.py)

原始DataFrame:
      Name  Age         City
0    Alice   24     New York
1      Bob   27  Los Angeles
2  Charlie   22      Chicago
3    David   32      Houston

年龄大于25的行:
    Name  Age         City
1    Bob   27  Los Angeles
3  David   32      Houston

按年龄升序排序后的DataFrame:
      Name  Age         City
2  Charlie   22      Chicago
0    Alice   24     New York
1      Bob   27  Los Angeles
3    David   32      Houston

描述性统计量:
             Age
count   4.000000
mean   26.250000
std     4.349329
min    22.000000
25%    23.500000
50%    25.500000
75%    28.250000
max    32.000000

相关文章:

  • 第四天面试题
  • Kafka消息服务之Java工具类
  • 在局域网中连接Grafana数据源
  • 什么是Grok-3?技术特点,场景,潜在问题与挑战
  • 数据结构(第八章 排序算法)
  • 成人床垫更新关于 SOR/2016-183 和《纺织品贴标和广告法规》的合规
  • nginx ngx_http_module(10) 指令详解
  • YOLOv11-ultralytics-8.3.67部分代码阅读笔记-dataset.py
  • Windows编程:在 VS2019 里面,显示行号
  • petalinux高版本设置自动登录和开机自启动配置
  • 敏捷项目管理:适应快速变化的项目环境
  • 武汉小米 Java 岗位一二面校招面经
  • Bazel 教程
  • MyBatis 中 SqlMapConfig 配置文件详解
  • HTML/CSS中交集选择器
  • 前七章综合练习
  • 集合 数据结构 泛型
  • Element UI常用组件
  • 知识库-登陆接口
  • 《论语别裁》第01章 学而(03) 四书五经的假面目
  • 微软将在全球裁员6000人,目标之一为减少管理层
  • 国台办:民进党当局刻意刁难大陆配偶,这是不折不扣的政治迫害
  • 波兰关闭俄罗斯驻克拉科夫领事馆
  • 中美日内瓦经贸会谈联合声明
  • 行知读书会|换一个角度看见社会
  • 李公明 | 一周画记:印巴交火会否升级为第四次印巴战争?