Python 虚拟环境 + 嵌入式 + 编译pyd 部署方案

Python 虚拟环境 + 嵌入式 + 编译pyd 部署方案

  • 开发阶段 - 直接发源码版本
    • 1. 在虚拟环境下开发 Python 项目
  • 部署阶段
    • 1. 创建项目文件夹
    • 2. 准备嵌入器 Python 解释器
    • 3. 处理第三方库
    • 4. 修改 ._pth 文件添加 Python 运行环境
    • 5. 添加启动 bat 脚本
  • 目录结构
  • 开发阶段 - 编译 pyd 再发布版本
    • 安装 Cythonize
      • 1. pip 安装 Cython
      • 2. C编译环境
    • 使用 Cythonize
    • 创建 bat 批量编译
    • 清理编译垃圾
  • 参考资料

开发阶段 - 直接发源码版本

1. 在虚拟环境下开发 Python 项目

在开发电脑上,我们使用虚拟环境来开发我们的Python项目。
目的就是把所有此项目所需的第三方库,安装在一个单独的虚拟环境中。
方便一波带走。

部署阶段

接来下我们来到需要部署的客户电脑,以嵌入式Python解释进行我们的代码。
这样适应于一些不能安装Python的情况。
以下步骤适应离线部署,因为我们所有东西都是打包直接复制过去的,不需要通过网络再获取其它东西。
当然我这个项目比较简单,如果用了一个刁钻的依赖,可能还要在此基础上,单独处理一下。

1. 创建项目文件夹

先创建个空文件 my_project 来放整个项目:(当然名称随意)

  1. Python解释器
  2. 项目代码
  3. 启动bat文件
  4. 其它相关文件

2. 准备嵌入器 Python 解释器

  1. 解压 python-3.11.5-embed-amd64.zip 为 python-3.11.5-embed-amd64 放到 my_project 目录
  2. 嵌入式Python的版本最好和我们开发环境的版本保持一至。
  3. 我偷懒不改名了,大家可以随意改成 python-3.11.5py3115-embed。。。

3. 处理第三方库

将开发机虚拟环境下的 site-packages 目录复制到 my_project 目录中
3.1. venv\Lib\site-packages中就是虚拟环境下安装的第三方库

4. 修改 ._pth 文件添加 Python 运行环境

修改 python-3.11.5-embed-amd64 目录下的 python311._pth 文件内容,加入一些所需路径。
311 对应 python 的版本,版本不同文件名会不一样。

  • 修改前
    python311.zip
    .# Uncomment to run site.main() automatically
    #import site
    
  • 修改后
    python311.zip
    .
    # 这是 my_project
    ..
    # 这里面是我们项目代码
    ..\src
    # 这是虚拟环境下的第三方库
    ..\site-packages
    # 这里去掉注释
    import site
    
    注意: 这里只要这里相对路径指对就行了。可以按自己需要调整目录解构。

5. 添加启动 bat 脚本

@echo off
chcp 65001
python-3.11.5-embed-amd64\python.exe src\main.py
pause
  1. my_project 创建批处理 run.bat 用嵌入式的 python-3.11.5-embed-amd64\python.exe 来执行 py
  2. chcp 65001 是使用 utf-8 编码,解决批处理中文乱码问题。

目录结构

my_project 根目录
│  run.bat 启动脚本
│  
├─python-3.11.5-embed-amd64 # 嵌入式 Python 解释器
│     嵌入式 Python 相关文件
│      
├─site-packages # 虚拟环境下安装好的第三方库目录
│ └─  虚拟环境下安装好的第三方库文件
│          
└─src│  main.py│  __init__.py│  └─package # 自定义包│  __init__.py├─service│   __init__.py│  └─ 业务代码│          └─utils│  __init__.py└─ 工具类

开发阶段 - 编译 pyd 再发布版本

上面我们直接将 src 目录复制到客户电脑去跑。但有时可能不方便直接给源码,需要稍微处理一下。
这时可以用 cythonize.py 编译成 .pyd

  1. 使用时 整个 src 目录解构保持不变,只是把 py 全换成 pyd
  2. sql_util.py 编译成 pyd 后名字类似这样 sql_util.cp311-win_amd64.pyd 这不影响导入,直接用就行。
    (我们严格遵循解释器使用相同版本的原则,所以不用改成 sql_util.pyd)

安装 Cythonize

1. pip 安装 Cython

CythonizeCython 的一个组件,安装 Cython 会自动安装 cythonize 工具。
(时刻牢记,我们要在为当前项目创建的虚拟环境下安装,免得污染全局)

pip install Cython

2. C编译环境

Cythonize 需要 C编译环境
gcc -v 查看有没有。我这里之前装了。没装的同学按这个来:MSYS2 安装 gcc、make 很简单。

(venv) D:\my_project>gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=D:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/13.2.0/lto-wrapper.exe
Target: x86_64-w64-mingw32
Configured with: ../gcc-13.2.0/configure --prefix=/mingw64 --with-local-prefix=/mingw64/local --build=x86_64-w64-mingw32 --host=x86_64-w64-mingw32 --target=x86_64-w64-mingw32 --with-native-system-header-dir=/mingw64/include --libexecdir=/mingw64/lib --enable-bootstrap --enable-checking=release --with-arch=nocona --with-tune=generic --enable-languages=c,lto,c++,fortran,ada,objc,obj-c++,jit --enable-shared --enable-static --enable-libatomic --enable-threads=posix --enable-graphite --enable-fully-dynamic-string --enable-libstdcxx-filesystem-ts --enable-libstdcxx-time --disable-libstdcxx-pch --enable-lto --enable-libgomp --disable-libssp --disable-multilib --disable-rpath --disable-win32-registry --disable-nls --disable-werror --disable-symvers --with-libiconv --with-system-zlib --with-gmp=/mingw64 --with-mpfr=/mingw64 --with-mpc=/mingw64 --with-isl=/mingw64 --with-pkgversion='Rev3, Built by MSYS2 project' --with-bugurl=https://github.com/msys2/MINGW-packages/issues --with-gnu-as --with-gnu-ld --disable-libstdcxx-debug --with-boot-ldflags=-static-libstdc++ --with-stage1-ldflags=-static-libstdc++
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 13.2.0 (Rev3, Built by MSYS2 project)

使用 Cythonize

  • 编译当前目录下指定文件 main.py, sql_util.py(可以看到它是支持多个文件的)
    cythonize -i main.py sql_util.py
    
  • 编译当前目录下所有 .py 但是排除 __init__.py
    cythonize -i *.py --exclude "__init__.py"
    

结果会生成到 my_project\build 目录中。

创建 bat 批量编译

my_project 下创建 编译PYD.bat
它将自动遍历 src 编译所有 .py 但是我排除了入口脚本 main.py__init__.py 如果你有需要 __init__.py 也可以不排除。

注意:最终我们的入口脚本 main.py 还是保持源码方式用来执行。.pyd 只能作为包导入,不能直接执行。

@echo off
chcp 65001>nul
rem 先激活虚拟环境
call venv\Scripts\activate.batecho ----------------- py >>> pyd 编译开始 -----------------
setlocal enabledelayedexpansionrem 设置起始目录,当前目录下的 src
set "startDir=src"rem 根目录 src 在循环外单独处理
pushd "%startDir%"
echo 开始编译目录: "%cd%"
cythonize -i *.py --exclude "__init__.py" 
popdrem 使用for /d /r循环遍历所有子目录
for /d /r "%startDir%" %%i in (*) do (rem 检查目录名是否为__pycache__if /i "%%~ni" NEQ "__pycache__" (rem 不是__pycache__,则进入并打印目录名pushd "%%i"echo 开始编译目录: "%%i"rem 使用 --exclude 排除文件,此参数可以多次使用cythonize -i *.py --exclude "main.py" --exclude "__init__.py" popd)
)
endlocal
echo ----------------- py >>> pyd 编译完成 -----------------
pause

清理编译垃圾

调试过程中我们肯定会反复编译,下面这个脚本用于清理编译生成的文件:.c, .pyd
是的你没看错 .pyd 也一起删。
因为上面编译时它会在 my_project 通过成一个 build 文件夹,编译结果会按 src 中的目录解构存放。
所以在我们源码目录 src 下生成的这些,我们是不需要的。

@echo off
chcp 65001 >nul
echo ----------------- 开始清理 -----------------
setlocal enabledelayedexpansionrem 设置起始目录
set "startDir=src"rem 递归删除所有.c和.pyd文件
for /r "%startDir%" %%f in (*.c *.pyd) do (del "%%f"echo 删除: %%f
)endlocal
echo ----------------- 清理完成 -----------------
pause
my_project 根目录
│  编译PYD.bat
│  清理PYD.bat
└─build│	嵌入式 Python 相关文件│      └─lib.win-amd64-cpython-311│          └─src │ 这下面就和 src 目录结果一样了			

参考资料

Python Releases for Windows
笑虾:C++ 开发 + VSCode 调试

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/851437.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue-router的导入和使用

关于如何在 Vue.js 中导入和使用 vue-router 的基本例子。 首先,确保你已经安装了 vue-router。你可以通过 npm 或 yarn 来安装: npm install vue-router # 或者 yarn add vue-router 然后,在你的 Vue 项目中,你可以按照以下…

FuTalk设计周刊-Vol.039

🔥AI漫谈 热点捕手 1、AI视频生成工具大PK | Runway Gen-2、Pika、Moonvalley和W.A.L.T的文字生视频对比评测 AI届的学术大牛李飞飞最近推出了用于生成逼真视频的扩散模型W.A.L.T。效果很不错,不过目前还未开放公网的访问。于是我萌生了一个想法&#…

气体流量的换算

测量气体流量时,往往需要进行温压补偿。我们可以选择Nm:/h和m3/h作为测量单位,二者之间如何换算呢?在标准状态下,即温度为0℃℃(273.15K)和压力为1个标准大气压(101.325kPa)时,气体的体积被称为Nm3(标方),N代表标准条…

音视频主要概念

文章目录 常用的一些概念主要概念1主要概念2I帧P帧B帧 常用视频压缩算法 小结 常用的一些概念 主要概念1 视频码率:kb/s,是指视频文件在单位时间内使用的数据流量,也叫码流率。码率越大,说明单位时间内取样率越大,数…

rust asyn和await pin unpin加精!!!

15-探讨为什么Pin在Rust异步编程中如此重要 | Databend_哔哩哔哩_bilibili 能不能Pin住,取决于T是否实现了Unpin,如果实现了Unpin,那么Pin不住 Pin不能pin住u32等基础变量 编译器为async和await生成结构体实现了!Unpin 结构体中使用引用要…

智能合约中Gas限制和DoS攻击漏洞

Gas限制和DoS攻击 Gas限制和DoS(Denial of Service,拒绝服务)攻击是在区块链和智能合约环境下常见的安全威胁,尤其是对于像以太坊这样的平台,其中Gas是一种用于衡量执行智能合约成本的单位。Gas机制设计的初衷是为了防…

62- 读写文件详解

一 读写文本文件(QFile 类) Qt 开发中的 QFile 类支持对文件进行读取、写入、删除、复制 重命名等相关操作,它既可以操作文本文件,也可以操作二进制文件。 #include <QCoreApplication> #include <QFile> #include <QDebug> int main(int argc, char *argv…

Java 18新特性全览:探索Java世界的最新进化!

随着技术的不断演进&#xff0c;Java作为历史悠久的编程语言&#xff0c;始终保持着其时代的前沿性。Java 18的发布&#xff0c;又一次展示了这门语言的强大生命力和不断创新的能力。在本文中&#xff0c;我们将深入探讨Java 18带来的那些令人兴奋的新特性&#xff0c;助你领略…

HTML+CSS 交互式开关按钮

效果演示 实现了一个交互式开关按钮的效果,包括一个标签和两个选项(Yes和No),当用户点击其中一个选项时,按钮会发生动画效果,同时选中的选项会被高亮显示。整个按钮的样式采用了渐变背景色、圆角边框、阴影等元素,使得按钮看起来更加美观。 Code HTML <!DOCTYPE ht…

下拉框数据被遮挡 且 后续数据无法下拉的 解决方法

目录 前言1. 问题所示2. 原理分析3. 解决方法3.1 添加空白版2.2 调整z-index2.3 父容器的溢出属性2.4 调整样式属性4. 效果图前言 小程序使用的是Uniapp,原理都差不多,索性标题就不标注Uniapp(小程序) 对于该问题调试了一个晚上,最终解决,对此记录下来 1. 问题所示 执…

JVM常用概念之线程本地分配缓冲区(ThreadLocal Allocation Buffer,TLAB)

当实例化一个Java类时&#xff0c;运行时环境必须为相关实例分配存储空间&#xff0c;在JRE中此存储空间分配操作是由内存管理器实现的&#xff08;其实是JVM的垃圾回收器&#xff09;&#xff0c;由于内存管理器通常使用与运行时目标语言不同的语言编写&#xff08;例如&#…

图片转pdf在线网站,图片转pdf在线网址,工具软件

在现代办公和学习环境中&#xff0c;图片转PDF的操作已变得日益重要。无论是为了存档、分享还是打印&#xff0c;将图片转换为PDF格式都能带来诸多便利。本文将详细介绍几种常用的图片转PDF方法。 打开 “轻云pdf处理官网” &#xff0c;上传图片。 图片上传完成后&#xff0…

AI学习指南机器学习篇-决策树在python中的实现

AI学习指南机器学习篇-决策树在Python中的实现 机器学习是人工智能领域中的重要分支&#xff0c;它涉及许多复杂的概念和技术。在机器学习的算法中&#xff0c;决策树是一种常用的监督学习方法&#xff0c;它可以帮助我们预测未来事件的发生或者分类数据。本篇文章将介绍如何使…

springCloudAlibaba之分布式事务组件---seata

Seata Sea学习分布式事务Seata二阶段提交协议AT模式TCC模式 Seata服务搭建 Sea学习 事务&#xff1a;事务是访问数据库并更新数据库中各项数据的一个程序执行单元。在关系数据库中&#xff0c;一个事务由一组或多组SQL语句组成。事务应该具有4个属性&#xff1a;原子性、一致性…

C语言——预编译处理

一、头文件处理 1)头文件#ifndef/define/endif关键字用法 #ifndef/define/endif 主要用于防止同一头文件被多次引用,避免重复定义同一个变量或函数。当一个头文件被多个源文件引用时,可以使用#ifndef/define/endif 结构确保其中的代码只会被编译一次,避免出现重定义的错误…

LabVIEW轴承试验机测控系统

开发了一种基于LabVIEW软件开发的大功率风电机组增速箱轴承试验机测控系统。系统主要用于模拟实际工况&#xff0c;进行轴承可靠性分析&#xff0c;以优化风电机组的性能和可靠性。通过高度自动化的测控系统&#xff0c;实现了对试验机的精确控制&#xff0c;包括速度、振动、温…

Unity 笔试题分享

1. 请回答以下代码片段执行时是否会产生堆内存分配 a. void SetChar(string s){s.Replace(b, d);}b. void Update(Transform t){t.localPosition new Vector3(0, 0, 0);}c、 int Sum(List<int> l){int total 0;foreach (int i in l){total i;} return total;}d…

金融数据中心能力建设指引

金融数据中心能力建设指引 金融数据中心能力建设指引旨在通过高标准的基础设施建设、完善的数据管理、强大的信息安全防护和业务连续性规划&#xff0c;确保数据中心具备高效、安全、可靠的运行能力&#xff0c;支持金融业务的稳定发展。该指引强调技术创新、标准化管理、人才…

大数据湖一体化运营管理建设方案(49页PPT)

方案介绍&#xff1a; 本大数据湖一体化运营管理建设方案通过构建统一存储、高效处理、智能分析和安全管控的大数据湖平台&#xff0c;实现了企业数据的集中管理、快速处理和智能分析。该方案具有可扩展性、高性能、智能化、安全性和易用性等特点&#xff0c;能够为企业数字化…

ios 获取图片的一部分区域

可以使用如下的代码&#xff1a; // get part of the image - (UIImage *)getPartOfImage:(UIImage *)img rect:(CGRect)partRect {CGImageRef imageRef img.CGImage;CGImageRef imagePartRef CGImageCreateWithImageInRect(imageRef, partRect);UIImage *retImg [UIImage i…