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

将 py 文件编译成 pyd 文件

文章目录

  • 一、简介
    • 1.1、Python中的文件类型:.py + .pyc + .pyd
    • 1.2、基本原理
      • 1.2.1、函数详解:Extension() —— 用于定义扩展模块(C/C++ 扩展)的类
      • 1.2.2、函数详解:setup() —— 用于配置和构建包的函数
  • 二、构建过程
    • 2.0、环境配置
    • 2.1、第一步:构建 setup.py 脚本(需手动指定)
      • 2.1.1、构建 setup.py 脚本(方式一):基于 setup() 函数
      • 2.1.2、构建 setup.py 脚本(方式二):基于 Extension() + setup() 函数
    • 2.2、第二步:将 .py 文件编译成 .pyd 文件:python setup.py build_ext --inplace
    • 2.3、pyd 模块的导入与使用
  • 四、(批量)将 .py 文件编译成 .pyd 文件(自动执行)

一、简介

1.1、Python中的文件类型:.py + .pyc + .pyd

py是明文且易读,pyc不易读但易被反编译,pyd不易读且不易被反编译(用于代码加密)。

  • 源代码文件 (.py):Python 的源代码文件以 .py 为扩展名,其中包含了 Python 代码的文本内容。这些文件可以直接编辑,并且可以通过 Python 解释器执行。
  • 字节码文件 (.pyc)当 Python 的源代码文件被导入时,Python 解释器会将其编译成字节码,并将字节码保存在以 .pyc 为扩展名的文件中。这些文件是 Python 解释器优化后的版本,用于加快导入模块的速度。如果 Python 源代码文件发生了改变,Python 解释器会自动重新生成对应的 .pyc 文件。
    • 使用 py_compile 将 .py 文件编译成 .pyc 文件python -m py_compile xxx.py
      • 特点:难以阅读,但很容易被反编译工具反编译回可读的源代码(如: uncompyle6)。
  • 动态链接库 (.pyd):在 Windows 平台上,Python 的扩展模块通常以 .pyd 为扩展名,它们是用 C 或 C++ 编写的,并且提供了对其他语言编写的库和系统调用的接口。.pyd 文件通常包含了用 C 或 C++ 编写的扩展模块,它们可以通过 Python 解释器导入,并且提供了对其它语言编写的库和系统调用的接口。
    • 使用 Cython 将 .py 文件编译成 C 扩展模块,从而生成二进制 .pyc 文件python setup.py build_ext
      • 特点:提高了反编译的难度。但反汇编工具仍可以对二进制文件进行逆向工程,尽管难度更大。编译过程复杂,可能需要配置额外的工具链。

1.2、基本原理

  • 使用函数 Extension() + setup() 构建 setup.py 扩展模块
  • 使用命令 python setup.py build_ext --inplace 生成扩展模块文件
    • Windows平台下,生成 .pyd 文件
    • Unix/Linux平台下,生成 .so 文件

1.2.1、函数详解:Extension() —— 用于定义扩展模块(C/C++ 扩展)的类

"""#########################################################################
# 函数功能:Python 中用于定义扩展模块(C/C++ 扩展)的类。
#         ———— 通过 Extension 类,可以指定扩展模块的名称、源文件的路径、编译选项等信息,从而在构建扩展模块时提供必要的配置。
# 函数说明:Extension(name, sources, include_dirs=None, define_macros=None, undef_macros=None,
#                   library_dirs=None, libraries=None, runtime_library_dirs=None,
#                   extra_objects=None, extra_compile_args=None, extra_link_args=None, export_symbols=None,
#                   swig_opts=None, depends=None, language=None)
# 参数说明:
#       - name:           扩展模块的名称,即在 Python 中导入时使用的名称。
#       - sources:        源文件的路径列表,可以是 .c、.cpp、.pyx 等类型的文件。
#         include_dirs:   包含的头文件目录列表。
#         define_macros:  宏定义列表,例如 [(<macro_name>, <macro_value>)]。
#         undef_macros:   要取消定义的宏列表。
#         library_dirs:   库文件目录列表。
#         libraries:      要链接的库文件列表。
#         runtime_library_dirs:运行时库文件目录列表。
#         extra_objects:  额外的目标文件列表。
#         extra_compile_args:额外的编译选项列表。
#         extra_link_args:额外的链接选项列表。
#         export_symbols: 导出的符号列表。
#         swig_opts:      用于 SWIG 扩展的额外选项。
#         depends:        依赖的文件列表。
#         language:       源文件的编程语言,例如 'c' 或 'c++'。
#########################################################################"""

1.2.2、函数详解:setup() —— 用于配置和构建包的函数

"""#########################################################################
# 函数功能:Python 中用于配置和构建包的函数,通常在 setup.py 脚本中使用。
#         ———— 通过 setup 函数,可以指定包的名称、版本、作者信息、依赖关系、入口点等信息,并配置构建和安装过程。
# 函数说明:setup(**attrs)
# 参数说明:
#         attrs:一个字典,包含了配置信息和选项。常用的参数包括:
#           - name:包的名称。
#             version:包的版本号。
#             author:包的作者名称。
#             author_email:作者的电子邮箱地址。
#             description:包的简要描述。
#             long_description:包的详细描述。
#             packages:包含的子包列表。
#             install_requires:依赖的其他包列表。
#             entry_points:包的入口点。
#           - ext_modules:扩展模块列表。
#             package_data:包含的数据文件列表。
#             scripts:可执行脚本文件列表。
#             classifiers:包的分类列表。
#             license:包的许可证。
#########################################################################"""

二、构建过程

在这里插入图片描述

2.0、环境配置

pip install python
pip install cython

2.1、第一步:构建 setup.py 脚本(需手动指定)

  • 报错提示error: unknown file type '.py' (from 'F:\py\sample.py')
  • 原因分析:试图将 Python 源文件直接传递给 Extension 对象,而它期望的是 C/C++ 源文件。
  • 解决方案:使用 Cython 的 cythonize() 函数,将 Python 文件转换为 C/C++ 扩展模块,然后再将其传递给 Extension 对象。

2.1.1、构建 setup.py 脚本(方式一):基于 setup() 函数

from setuptools import setup
from Cython.Build import cythonizesetup(name='my_module', ext_modules=cythonize(r'F:\py\temp\my_module.py'))

2.1.2、构建 setup.py 脚本(方式二):基于 Extension() + setup() 函数

from setuptools import setup, Extension
from Cython.Build import cythonize# 定义(单个)扩展模块
ext_modules = Extension(name="my_module", sources=[r"F:\py\temp\my_module.py"])"""
# 定义(多个)扩展模块
ext_modules = [Extension(name="my_module1", sources=[r"F:\py\temp\my_module1.py"]),Extension(name="my_module2", sources=[r"F:\py\temp\my_module2.py"]),# 添加更多的扩展模块...
]
"""# 使用 cythonize() 函数将扩展模块编译为 C/C++ 扩展模块,并将其传递给 ext_modules 参数。
setup(ext_modules=cythonize(ext_modules))

2.2、第二步:将 .py 文件编译成 .pyd 文件:python setup.py build_ext --inplace

在命令行中,执行命令:python setup.py build_ext --inplace

  • python: Python 解释器可执行文件的路径(默认使用环境变量的配置路径)。
  • setup.py:Python 脚本文件,用于定义 Python 包的构建和安装方式。通常,这个文件会包含一些元数据和构建指令,以告诉 setuptools(Python 的一个包管理工具)如何构建和安装这个包。
  • python setup.py:使用 python 命令,执行 Python 脚本文件。
  • build_ext: Python 定义的一个命令,用于构建扩展模块。
  • --inplace:可选。表示编译后的 .pyd 文件保存在默认的构建目录中,复制一份到当前目录中。
    • 默认路径:build\lib.win-amd64-cpython-39\my_module.cp39-win_amd64.pyd

编译后,将在当前路径下生成以下文件:

  • (1)build 文件夹:包含编译过程中的所有中间文件和最终的扩展模块库文件。
  • (2)使用 Cython 工具,将 .py 文件编译成 .c 文件:my_module.c
  • (3)编译后生成二进制 .pyc 文件:my_module.pyd

在这里插入图片描述

2.3、pyd 模块的导入与使用

# my_module.pydef hello():return "Hello, World!"def add(a, b):return a + bdef subtract(a, b):return a - b

同一个文件的py与pyd不要保存在一个路径下,否则无法区别是调用的py模块还是pyd模块。

# main.pyimport my_module  # 导入pyd模块print(my_module.hello())  # 输出: Hello, World!
print(my_module.add(1, 2))  # 输出: 3
print(my_module.subtract(1, 2))  # 输出: -1

四、(批量)将 .py 文件编译成 .pyd 文件(自动执行)

将批量 .py 文件编译成 .pyd 文件。

"""##############################################################
# 功能:将批量.py文件编译成.pyd文件。
##############################################################"""
import os
import glob
import shutildef py2pyd(one_all, del_py, paths):def compile_single_py(path):folder_path = os.path.dirname(path)  # 获取文件夹路径file_path = os.path.split(path)[1]  # 获取文件名(包括后缀)os.chdir(folder_path)  # 改变当前工作目录为 folder_path 所指定的目录############################################################################## 在在当前路径下,自动生成一个setup.py文件with open('setup.py', 'w') as f:f.write('from setuptools import setup\n')f.write('from Cython.Build import cythonize\n')f.write(f"setup(name='test', ext_modules=cythonize('{file_path}'))")# 开始编译setup.py(编译结束,将在当前路径下生成:xxx.cp38-win_amd64.pyd + xxx.c + build文件夹)os.system('python setup.py build_ext --inplace')#############################################################################filename = file_path.split('.py')[0]  # 获取文件名(没有后缀)pyd_name = f'{folder_path}\\{filename}.pyd'  # 获取.pyd文件路径# 若在当前路径下,已存在同名的.pyd文件,则删除。if os.path.exists(pyd_name):os.remove(pyd_name)"""修改.pyd文件名"""# 获取.pyd文件的文件名 ———— 如: xxx.cp38-win_amd64.pydamd64_pyd = glob.glob(filename + "*.pyd")  # glob.glob: 获取指定模式下的所有文件名列表print("*"*100)print(f"{amd64_pyd[0]} has been generated.")# 修改.pyd文件的文件名 ———— 如: xxx.pyd,即删除冗余的.cp38-win_amd64os.rename(amd64_pyd[0], pyd_name)  # os.rename(old_name, new_name): 将old_name重命名为new_name."""删除过程文件"""os.remove('setup.py')  # 删除自动生成的setup.py文件os.remove(f'{filename}.c')  # 删除自动生成的xxx.c文件shutil.rmtree(os.path.join(folder_path, 'build'))  # 删除自动生成的build文件夹及其所有内容。"""删除.py源文件 ———— 无法恢复,慎用!"""if del_py == 'del':os.remove(file_path)def get_all_file(path):"""遍历给定目录下的所有.py文件,包含子目录里的.py文件"""for root, dirs, files in os.walk(path):for name in files:if name.endswith(".py"):compile_single_py(os.path.join(root, name))if one_all == 'one':compile_single_py(paths)  # 若为单个.py文件,则直接执行。else:get_all_file(paths)       # 若为多个.py文件,则遍历所有文件,并单个执行。"""##############################################################
# 参数说明:
#         all:      批量编译指定目录及其子目录下的所有.py文件
#         one:      编译单个.py文件
#         del:      编译完成后删除源文件。注意:无法恢复,谨慎使用!
#         nodel:    编译完成后保留源文件。
##############################################################""""""##############################################################
# 示例1:批量编译指定目录及其子目录下的所有.py文件
#         py2pyd('all', 'nodel', r'D:\\PYTHON\\toPYD\\test')  # 不删除源文件
#         py2pyd('all', 'del', r'D:\\PYTHON\\toPYD\\test')  # 删除源文件
# 
# 示例2:编译单个.py文件
#         py2pyd('one', 'nodel', r'D:\\PYTHON\\toPYD\\test\\mycode.py')  # 不删除源文件
#         py2pyd('one', 'del', r'D:\\PYTHON\\toPYD\\test\\mycode.py')  # 删除源文件
##############################################################"""
if __name__ == '__main__':try:from setuptools import setup, Extensionfrom Cython.Build import cythonizepy2pyd('one', 'nodel', r'F:\py\pyinstaller\test.py')except ImportError:print("cython模块未安装,安装命令:pip install cython")

相关文章:

  • WHAT - Typescript 中 structural-type-system 结构类型系统
  • Harmony os Next——Ble蓝牙模块
  • Linux 自动化升级Jar程序,指定Jar程序版本进行部署脚本
  • 【银角大王——Django课程——Ajax请求】
  • FJSP:波搜索算法(WSA)求解柔性作业车间调度问题(FJSP),提供MATLAB代码
  • 压测工具sysbench
  • 性能监控计算——封装带性能计算并上报的npm包(第三章)
  • 【TIPs】 Visual Stadio 2019 中本地误使用“git的重置 - 删除更改 -- hard”后,如何恢复?
  • 初识SDN
  • python-数据可视化(总)
  • Qt for Android
  • 代码随想录--哈希表--两数之和
  • 关于模拟信道和数字信道根本区别的探讨
  • 常用maven - jar 下载与 安装
  • 国产可视化爬虫助力AI大模型训练:精准爬取汉语词典
  • 2017 前端面试准备 - 收藏集 - 掘金
  • Computed property XXX was assigned to but it has no setter
  • CSS 提示工具(Tooltip)
  • ECS应用管理最佳实践
  • Hibernate最全面试题
  • JavaScript对象详解
  • JavaScript学习总结——原型
  • Map集合、散列表、红黑树介绍
  • Mocha测试初探
  • Objective-C 中关联引用的概念
  • Otto开发初探——微服务依赖管理新利器
  • vue的全局变量和全局拦截请求器
  • Windows Containers 大冒险: 容器网络
  • 创建一种深思熟虑的文化
  • 发布国内首个无服务器容器服务,运维效率从未如此高效
  • 服务器之间,相同帐号,实现免密钥登录
  • 近期前端发展计划
  • 融云开发漫谈:你是否了解Go语言并发编程的第一要义?
  • 使用Envoy 作Sidecar Proxy的微服务模式-4.Prometheus的指标收集
  • 一些关于Rust在2019年的思考
  • 格斗健身潮牌24KiCK获近千万Pre-A轮融资,用户留存高达9个月 ...
  • ​MySQL主从复制一致性检测
  • ​RecSys 2022 | 面向人岗匹配的双向选择偏好建模
  • ​如何使用ArcGIS Pro制作渐变河流效果
  • ###项目技术发展史
  • #nginx配置案例
  • #stm32整理(一)flash读写
  • $refs 、$nextTic、动态组件、name的使用
  • (3) cmake编译多个cpp文件
  • (42)STM32——LCD显示屏实验笔记
  • (MTK)java文件添加简单接口并配置相应的SELinux avc 权限笔记2
  • (pycharm)安装python库函数Matplotlib步骤
  • (STM32笔记)九、RCC时钟树与时钟 第一部分
  • (附源码)计算机毕业设计ssm本地美食推荐平台
  • (九)信息融合方式简介
  • (十六)一篇文章学会Java的常用API
  • (四)Tiki-taka算法(TTA)求解无人机三维路径规划研究(MATLAB)
  • (四)事件系统
  • (算法)Game
  • (微服务实战)预付卡平台支付交易系统卡充值业务流程设计