杰瑞科技汇

Python如何编译成Python可调用的库?

什么是“编译”?

我们需要明确这里的“编译”是什么意思,在 Python 的世界里,我们通常说的“编译”并不是指像 C++ 或 Java 那样编译成机器码,而是指将 .py 源代码文件转换成一种字节码 文件(.pyc 文件)。

Python如何编译成Python可调用的库?-图1
(图片来源网络,侵删)

这样做的主要目的不是提升运行速度(虽然字节码加载比源码快),而是:

  1. 隐藏源代码:将你的核心算法或商业逻辑保护起来,防止用户直接查看和修改 .py 文件。
  2. 提升模块加载速度:Python 解释器在导入模块时,会先检查是否存在对应的 .pyc 文件,如果存在且比 .py 文件新,就会直接加载字节码,跳过语法解析和编译的步骤,从而略微提升启动速度。
  3. 分发便利性:可以只分发 .pyc 文件和必要的非代码文件(如 .so, .dll),而不需要分发 .py 源码。

使用 PyInstaller 打包成可执行文件或库包 (最推荐)

这是最常用、最简单,也是最强大的方法,PyInstaller 可以将你的 Python 程序(包括所有依赖)打包成一个独立的可执行文件(在 Windows 上是 .exe,在 macOS 上是 .app,在 Linux 上是二进制文件),或者一个可以直接被 import 的库。

场景:你想把一个 my_module.py 打包,让其他项目可以直接 import my_module 来使用。

步骤 1: 安装 PyInstaller

pip install pyinstaller

步骤 2: 准备你的 Python 代码

假设你有一个核心模块 my_secret_module.py,里面包含你不想公开的函数。

Python如何编译成Python可调用的库?-图2
(图片来源网络,侵删)

my_secret_module.py

# 这是一个你想保护的模块
def secret_function(a, b):
    """这是一个核心计算函数,不想让别人看到实现细节"""
    print("正在执行秘密计算...")
    # 这里可能包含复杂的算法
    result = a * a + b * b + 2 * a * b
    return result
def public_function(x):
    """这是一个公开的函数,它调用了秘密函数"""
    print("公开函数被调用了")
    return secret_function(x, x)

步骤 3: 使用 PyInstaller 打包

打开终端,进入 my_secret_module.py 所在的目录,然后运行以下命令:

# -D 或 --onedir: 打包成一个文件夹(包含可执行文件和所有依赖),推荐用于库
# --name: 指定打包后的文件夹名称
# --distpath: 指定输出目录 (可选)
# --specpath: 指定 .spec 文件位置 (可选)
pyinstaller -D --name=my_cool_lib my_secret_module.py

命令解释:

  • -D--onedir: 创建一个目录,里面包含一个可执行文件和所有需要的动态链接库等,这种方式更适合作为“库”来分发,因为它保留了文件结构,方便 Python 解释器找到模块。
  • --name=my_cool_lib: 给打包后的文件夹起一个你想要的名字。
  • my_secret_module.py: 这是你要打包的入口文件。

步骤 4: 查看结果和安装

运行命令后,你会看到几个新文件夹:

Python如何编译成Python可调用的库?-图3
(图片来源网络,侵删)
  • build/: 存放临时文件,可以删除。
  • dist/: 这是最重要的文件夹,里面会有一个名为 my_cool_lib 的文件夹。
  • my_secret_module.spec: PyInstaller 的配置文件,可以用于更复杂的打包配置。

进入 dist/my_cool_lib 目录,你会看到类似这样的结构:

dist/my_cool_lib/
├── my_cool_lib/
│   ├── __init__.py         <-- Python 包的初始化文件
│   ├── my_secret_module.cpython-39.pyc  <-- 编译后的字节码文件!
│   ├── ... (其他依赖的 .pyc 文件)
│   └── lib/
│       └── ... (一些动态链接库 .dll 或 .so)
└── my_cool_lib.exe         <-- 一个可执行文件(Windows上)

如何安装和使用这个库?

  1. 安装:将 dist/my_cool_lib/my_cool_lib 这个内部的文件夹复制到你项目的 site-packages 目录下,或者使用 pip 安装。

    # 进入 dist 目录
    cd dist
    # 使用 pip 安装内部的文件夹
    pip install ./my_cool_lib/

    这会把 my_cool_lib 包安装到你的 Python 环境中。

  2. 使用:现在你可以在任何其他 Python 项目中像使用普通库一样使用它了。

main_app.py (调用方)

import my_cool_lib  # 直接导入打包后的包名
# 调用公开函数
result = my_cool_lib.public_function(10)
print(f"计算结果是: {result}")
# 注意:你无法直接导入 secret_function,因为它在 my_secret_module.py 中
# from my_cool_lib import secret_function  # 这会报错,除非你显式导出

使用 py_compile 模块 (手动编译)

这种方法最直接,就是手动生成 .pyc 文件,它非常简单,但功能有限,通常用于简单的模块分发。

场景:你只想快速把几个 .py 文件编译成 .pyc,然后一起分发。

步骤 1: 编译单个文件

import py_compile
py_compile.compile('my_secret_module.py', 'my_secret_module.pyc')

这会生成一个 my_secret_module.pyc 文件。

步骤 2: 编译一个包的所有文件

如果你有一个包,可以使用 Python 的 -m compileall 命令。

# 编译当前目录下的所有 .py 文件
python -m compileall .
# 编译指定目录
python -m compileall ./my_package/

如何分发和使用?

  1. 分发:你将 .pyc 文件和你的项目其他非代码文件(如配置文件、数据文件)一起打包。
  2. 使用:调用方需要将这个包含 .pyc 文件的目录结构放在 Python 的模块搜索路径中(site-packages),然后就可以 import 了。

缺点

  • 不跨版本.pyc 文件是与 Python 版本和操作系统强相关的,在 Python 3.9 上编译的 .pyc 文件不能在 Python 3.10 上使用。
  • 容易被反编译.pyc 文件可以被轻易地反编译回几乎可读的 Python 代码(使用 uncompyle6 等工具),安全性不高。
  • 不包含元数据:没有版本号、依赖信息等,不方便管理。

这种方法只适用于非常简单的、对安全性要求不高的内部工具。


使用 Cython 将 Python 代码编译成 C 扩展 (性能与安全兼顾)

如果你的目标是极致的性能更强的代码保护,Cython 是最佳选择,Cython 允许你写一种 Python 和 C 混合的语言,然后将其编译成 C 代码,并最终编译成 Python 可以调用的动态链接库(.so.pyd)。

场景:你有一个计算密集型的 Python 模块,既想让它跑得飞快,又想保护它的源码。

步骤 1: 安装 Cython 和必要的编译工具

pip install cython

你还需要安装 C 语言编译器,比如在 Windows 上是 Visual C++ Build Tools,在 Linux 上是 gcc

步骤 2: 创建 .pyx 文件

将你的 Python 代码改成 .pyx 后缀。

my_fast_module.pyx

# 定义一个 C 类型的变量,提升性能
def fast_function(int a, int b):
    """一个用 Cython 编写的、性能极高的函数"""
    cdef int result = a * a + b * b + 2 * a * b
    return result

步骤 3: 创建 setup.py 文件

这个文件是 Cython 的编译配置脚本。

setup.py

from setuptools import setup
from Cython.Build import cythonize
import os
# 获取当前目录名作为模块名
module_name = os.path.basename(os.getcwd())
setup(
    name=module_name,
    ext_modules=cythonize("my_fast_module.pyx"),
)

步骤 4: 编译

在终端运行:

python setup.py build_ext --inplace

步骤 5: 查看结果

如果成功,你会生成一个文件,如 my_fast_module.cpython-39-x86_64-linux-gnu.so (Linux) 或 my_fast_module.cp39-win_amd64.pyd (Windows),这个文件就是编译好的 C 扩展。

如何使用?

调用方可以直接 import 这个模块,就像导入普通 Python 模块一样,他们得到的只有 .so.pyd 文件,看不到你的 Python/Cython 源码。

main_app.py (调用方)

import my_fast_module
result = my_fast_module.fast_function(10, 20)
print(f"快速计算结果是: {result}")

优点

  • 性能大幅提升:对于计算密集型任务,性能可以媲美 C。
  • 代码保护性极强:C 源码(.c 文件)可读性差,.so/.pyd 文件更是无法直接查看逻辑。
  • 生成标准 Python 扩展:与 Python 解释器无缝集成。

缺点

  • 复杂度高:需要学习 Cython 语法,并且配置编译环境。
  • 有编译开销:每次修改代码后都需要重新编译。

总结与选择

方法 优点 缺点 适用场景
PyInstaller 简单易用,能处理所有依赖,跨平台,打包成标准库或单文件 启动速度略慢(因为是完整打包),最终文件体积较大 绝大多数场景,特别是当你想保护代码、方便分发,且不关心极致性能时,这是首选推荐
py_compile 最简单,无外部依赖 安全性低(易反编译),不跨版本,无元数据 简单的内部工具,或只想略微提升模块加载速度。
Cython 性能最高,代码保护性最强 复杂,需要C编译环境,有学习曲线 计算密集型任务,对性能和代码安全都有极高要求的库。

给你的建议:

  • 如果你想快速、方便地分发你的 Python 库并隐藏源码,请使用 PyInstaller,并选择 -D (onedir) 模式。
  • 如果你的库核心是大量科学计算或循环,并且你希望它跑得飞快,请考虑 Cython
  • 如果只是想临时编译几个文件,或者对安全性要求极低,可以用 py_compile
分享:
扫描分享到社交APP
上一篇
下一篇