.pyi
与.pyd
:Python
的DLL
(动态链接库)
一、为什么使用?
.pyd
的优势:
性能提升:C
扩展比纯Python
快1-2
倍(如循环密集型任务)。
代码保护:反编译难度高,适用于商业闭源项目。
跨语言集成:复用C/C++
生态的成熟库。.pyi
的优势:
开发效率:IDE
自动补全和类型提示减少编码错误。
文档化接口:明确模块功能,尤其适用于无源码的第三方库。
二、.pyd
文件的创建与使用
- 通常通过以下两种方式创建:
-
Cython
(推荐):-
编写 Python 代码:例如
example.py
。 -
转换为
.pyx
文件:将.py
文件重命名为.pyx
,或直接编写Cython
代码。 -
编写
setup.py
:from setuptools import setup from Cython.Build import cythonize setup(ext_modules=cythonize("example.pyx"))
-
编译生成
.pyd
:执行命令python setup.py build_ext --inplace
,生成example.pyd
-
-
手动编写
C/C++
扩展:- 编写
C/C++
代码,并遵循Python C API
规范(如函数注册和模块定义)。 - 使用
setuptools
编译,类似Cython
的流程。
- 编写
-
使用方法:
- 导入模块:将
.pyd
文件放在Python
搜索路径(如当前目录或sys.path
包含的路径),直接import example
即可调用其函数。 - 注意事项:
- 版本兼容性:
.pyd
需与Python
版本、操作系统架构(32/64 位
)完全匹配。 - 依赖处理:若依赖其他 DLL,需将路径添加到环境变量或使用
os.add_dll_directory()
。
- 版本兼容性:
- 导入模块:将
-
使用场景:
- 性能优化:
C/C++
编译的代码比纯Python
快,适合计算密集型任务(如科学计算)。 - 代码保护:
.pyd
是二进制文件,难以反编译,适合保护核心算法。 - 集成底层库:调用现有
C/C++
库或硬件接口。
- 性能优化:
三、.pyi
文件的创建与使用
- 创建方法(
.pyi
是类型提示存根文件,提供静态类型信息)
-
手动编写:根据模块接口定义类型,例如:
# example.pyi def add(a: int, b: int) -> int: ...
-
自动生成:使用工具如
mypy
的stubgen
或pyright
,根据代码或.pyd
生成类型提示。
- 使用方法
-
与模块同名:将
.pyi
文件与对应的.py
或.pyd
文件放在同一目录,IDE
和类型检查工具(如mypy
)会自动识别。 -
调试与补全:在
PyCharm、VS Code 等 IDE
中,.pyi
可提供代码补全和类型检查支持。
- 使用场景
- 静态类型检查:帮助发现类型错误(如参数类型不匹配)。
IDE
支持:为.pyd
等二进制模块提供可读的接口文档。- 代码维护:明确模块的输入输出类型,提升协作效率。
四、常见问题
.pyd
导入失败:- 原因:
Python
版本或架构不匹配、依赖缺失。 - 解决:检查编译环境,确保路径正确。
- 原因:
.pyi
类型提示无效:- 原因:文件命名错误或类型定义不匹配。
- 解决:确保
.pyi
与模块同名,且函数签名一致
五、工具推荐
Cython
:生成.pyd
的核心工具。mypy/stubgen
:生成和验证.pyi
文件。PyInstaller
:打包.pyd
为独立可执行文件。
六、示例01(使用Cython
)
- 文件目录结构:
sort_demo/
├── cython_sort.pyx # Cython 源码
├── setup.py # 编译脚本
├── cython_sort.pyd # 编译后生成
└── cython_sort.pyi # 类型提示
-
cython_sort.pyx
# cython_sort.pyx
import numpy as np
cimport numpy as cnp
from libc.stdlib cimport qsort
from enum import IntEnum # 添加这行
# Cython 内部使用的枚举
cdef enum CSortType:
BUBBLE_SORT = 0
QUICK_SORT = 1
COUNTING_SORT = 2
RADIX_SORT = 3
# Python 可见的枚举类
class SortType(IntEnum):
BUBBLE_SORT = CSortType.BUBBLE_SORT
QUICK_SORT = CSortType.QUICK_SORT
COUNTING_SORT = CSortType.COUNTING_SORT
RADIX_SORT = CSortType.RADIX_SORT
# 修正后的快速排序比较函数 - 添加 'nogil' 和 'noexcept'
cdef int compare(const void *a, const void *b) noexcept nogil:
cdef int a_val = (<int*>a)[0]
cdef int b_val = (<int*>b)[0]
return a_val - b_val
# 排序主函数
def sort(arr: list[int], sort_type: SortType) -> list[int]:
cdef cnp.ndarray[cnp.int_t, ndim=1] np_arr = np.array(arr, dtype=np.int32)
if sort_type == SortType.BUBBLE_SORT:
return bubble_sort(np_arr).tolist()
elif sort_type == SortType.QUICK_SORT:
return quick_sort(np_arr).tolist()
elif sort_type == SortType.COUNTING_SORT:
return counting_sort(np_arr).tolist()
elif sort_type == SortType.RADIX_SORT:
return radix_sort(np_arr).tolist()
else:
raise ValueError("未知排序类型")
# 冒泡排序(Cython 优化版)
cdef cnp.ndarray bubble_sort(cnp.ndarray[cnp.int_t, ndim=1] arr):
cdef int n = arr.shape[0]
cdef int *ptr = <int*>cnp.PyArray_DATA(arr)
cdef int i, j, temp
for i in range(n):
for j in range(0, n-i-1):
if ptr[j] > ptr[j+1]:
temp = ptr[j]
ptr[j] = ptr[j+1]
ptr[j+1] = temp
return arr
# 快速排序(基于标准库 qsort)
cdef cnp.ndarray quick_sort(cnp.ndarray[cnp.int_t, ndim=1] arr):
cdef int n = arr.shape[0]
cdef int *ptr = <int*>cnp.PyArray_DATA(arr)
# 在 nogil 块中调用 qsort
with nogil:
qsort(ptr, n, sizeof(int), &compare)
return arr
# 计数排序
cdef cnp.ndarray counting_sort(cnp.ndarray[cnp.int_t, ndim=1] arr):
cdef int n = arr.shape[0]
cdef int max_val = np.max(arr)
cdef cnp.ndarray[cnp.int_t, ndim=1] count = np.zeros(max_val+1, dtype=np.int32)
cdef cnp.ndarray[cnp.int_t, ndim=1] output = np.empty(n, dtype=np.int32)
cdef int *arr_ptr = <int*>cnp.PyArray_DATA(arr)
cdef int *count_ptr = <int*>cnp.PyArray_DATA(count)
cdef int *out_ptr = <int*>cnp.PyArray_DATA(output)
cdef int i, idx = 0
# 计数
for i in range(n):
count_ptr[arr_ptr[i]] += 1
# 输出
for i in range(max_val+1):
for j in range(count_ptr[i]):
out_ptr[idx] = i
idx += 1
return output
# 基数排序(支持负数)
cdef cnp.ndarray radix_sort(cnp.ndarray[cnp.int_t, ndim=1] arr):
cdef int n = arr.shape[0]
cdef int *ptr = <int*>cnp.PyArray_DATA(arr)
# 处理空数组
if n == 0:
return arr
# 查找最小值和最大值
cdef int min_val = ptr[0]
cdef int max_val = ptr[0]
cdef int i
for i in range(1, n):
if ptr[i] < min_val:
min_val = ptr[i]
if ptr[i] > max_val:
max_val = ptr[i]
# 创建临时数组
cdef cnp.ndarray[cnp.int_t, ndim=1] output = np.empty(n, dtype=np.int32)
cdef int *out_ptr = <int*>cnp.PyArray_DATA(output)
# 处理负数:添加偏移量使所有值为非负
cdef int offset = 0
if min_val < 0:
offset = -min_val
for i in range(n):
ptr[i] += offset
max_val += offset
# 基数排序主循环
cdef int exp = 1
cdef int j
cdef int count[10] # 声明计数数组
while max_val // exp > 0:
# 初始化计数数组
for j in range(10):
count[j] = 0
# 统计数字出现次数
for i in range(n):
j = (ptr[i] // exp) % 10
count[j] += 1
# 计算累计位置
for j in range(1, 10):
count[j] += count[j - 1]
# 根据计数构建输出数组(从后向前保持稳定性)
for i in range(n-1, -1, -1):
j = (ptr[i] // exp) % 10
count[j] -= 1
out_ptr[count[j]] = ptr[i]
# 将输出数组复制回原数组
for i in range(n):
ptr[i] = out_ptr[i]
# 移到下一位数字
exp *= 10
# 移除偏移量恢复原始值
if offset > 0:
for i in range(n):
ptr[i] -= offset
return arr
-
setup.py
from setuptools import setup
from Cython.Build import cythonize
import numpy as np
setup(
ext_modules=cythonize("cython_sort.pyx",
compiler_directives={'language_level': "3"}),
include_dirs=[np.get_include()]
)
-
编译命令
python setup.py build_ext --inplace
-
cython_sort.pyi
(类型提示)
from enum import Enum
class SortType(Enum):
BUBBLE_SORT: int
QUICK_SORT: int
COUNTING_SORT: int
RADIX_SORT: int
def sort(arr: list[int], sort_type: SortType) -> list[int]: ...
test_demo.py
from cython_sort import sort, SortType
import numpy as np
import time
import random
import sys
# 打印系统信息
print(f"Python版本: {sys.version}")
print(f"操作系统: {sys.platform}")
print(f"处理器: {sys.getwindowsversion().build if sys.platform == 'win32' else '未知'}")
# 生成测试数据
np.random.seed(42)
data_sizes = [1000, 5000, 10000] # 不同大小的数据集
results = {}
for size in data_sizes:
print(f"\n{'='*50}")
print(f"测试数据集大小: {size}")
print(f"{'='*50}")
data = np.random.randint(0, 10000, size).tolist()
print(f"原始数据长度:{len(data)}")
# 测试内置排序
start = time.perf_counter()
sorted_builtin = sorted(data)
elapsed_builtin = time.perf_counter() - start
print(f"Python内置排序耗时: {elapsed_builtin:.6f}秒")
# 测试各种排序算法
for sort_type in [SortType.BUBBLE_SORT, SortType.QUICK_SORT,
SortType.COUNTING_SORT, SortType.RADIX_SORT]:
# 复制数据以避免修改原始数据
test_data = data.copy()
start = time.perf_counter()
sorted_data = sort(test_data, sort_type)
elapsed = time.perf_counter() - start
# 验证排序结果
assert sorted_data == sorted_builtin, f"{sort_type}排序结果验证失败"
type_names = {
SortType.BUBBLE_SORT: "冒泡排序",
SortType.QUICK_SORT: "快速排序",
SortType.COUNTING_SORT: "计数排序",
SortType.RADIX_SORT: "基数排序"
}
# 计算相对于内置排序的速度比
speed_ratio = elapsed_builtin / elapsed if elapsed > 0 else float('inf')
print(f"{type_names[sort_type]}耗时: {elapsed:.6f}秒",
f"({speed_ratio:.2f}x {'快于' if speed_ratio > 1 else '慢于'}内置排序)")
# 存储结果用于后续分析
if sort_type not in results:
results[sort_type] = []
results[sort_type].append((size, elapsed))
# 添加测试负数和零的情况
print("\n测试边界情况:")
test_cases = [
("负数", [-5, -3, -1, -2, -4]),
("零", [0, 0, 0, 0, 0]),
("混合", [-5, 0, 5, -3, 3]),
("重复", [5, 3, 5, 1, 3, 1]),
("有序", [1, 2, 3, 4, 5]),
("逆序", [5, 4, 3, 2, 1]),
]
for name, data in test_cases:
print(f"\n测试用例: {name} - {data}")
# 内置排序
sorted_builtin = sorted(data)
# 测试各种排序算法
for sort_type in [SortType.QUICK_SORT, SortType.RADIX_SORT]:
test_data = data.copy()
sorted_data = sort(test_data, sort_type)
type_names = {
SortType.BUBBLE_SORT: "冒泡排序",
SortType.QUICK_SORT: "快速排序",
SortType.COUNTING_SORT: "计数排序",
SortType.RADIX_SORT: "基数排序"
}
assert sorted_data == sorted_builtin, f"{type_names[sort_type]}在{name}测试中失败"
print(f"{type_names[sort_type]}结果: {sorted_data}")
# 性能对比图表(可选)
try:
import matplotlib.pyplot as plt
print("\n生成性能对比图表...")
plt.figure(figsize=(12, 8))
for sort_type, times in results.items():
sizes = [size for size, _ in times]
durations = [time for _, time in times]
type_names = {
SortType.BUBBLE_SORT: "冒泡排序",
SortType.QUICK_SORT: "快速排序",
SortType.COUNTING_SORT: "计数排序",
SortType.RADIX_SORT: "基数排序"
}
plt.plot(sizes, durations, 'o-', label=type_names[sort_type])
# 添加内置排序参考线
builtin_times = [0.0001, 0.0005, 0.001] # 示例值,实际值会根据运行结果变化
plt.plot(data_sizes, builtin_times, 'k--', label="Python内置排序")
plt.xlabel('数据量')
plt.ylabel('耗时 (秒)')
plt.title('排序算法性能对比')
plt.legend()
plt.grid(True)
plt.yscale('log') # 对数尺度更好地展示差异
plt.savefig('sorting_performance.png')
print("图表已保存为 sorting_performance.png")
except ImportError:
print("\n未安装matplotlib,跳过图表生成")
- 测试结果

七、示例02(使用手动C扩展
)
- 文件结构
sort_demo/
├── sort_module.c # C 扩展源码
├── setup.py # 编译脚本
├── sort_module.pyd # 编译后生成
└── sort_module.pyi # 类型提示文件
sort_module.c
(完整实现)
#include "Python.h"
#include <stdlib.h>
#include <string.h>
#include <limits.h>
// 排序类型枚举
typedef enum
{
BUBBLE_SORT = 0,
QUICK_SORT = 1,
COUNTING_SORT = 2,
RADIX_SORT = 3
} SortType;
// 快速排序分区函数
static int partition(int arr[], int low, int high)
{
int pivot = arr[high];
int i = low - 1;
int j;
for (j = low; j <= high - 1; j++)
{
if (arr[j] < pivot)
{
i++;
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
int temp = arr[i + 1];
arr[i + 1] = arr[high];
arr[high] = temp;
return i + 1;
}
// 快速排序主函数
static void quick_sort_impl(int arr[], int low, int high)
{
if (low < high)
{
int pi = partition(arr, low, high);
quick_sort_impl(arr, low, pi - 1);
quick_sort_impl(arr, pi + 1, high);
}
}
// 冒泡排序
static void bubble_sort_impl(int arr[], int n)
{
int i, j;
for (i = 0; i < n - 1; i++)
{
for (j = 0; j < n - i - 1; j++)
{
if (arr[j] > arr[j + 1])
{
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
// 计数排序
static void counting_sort_impl(int arr[], int n)
{
if (n == 0)
return;
int i;
// 查找最大值和最小值
int min_val = arr[0];
int max_val = arr[0];
for (i = 1; i < n; i++)
{
if (arr[i] < min_val)
min_val = arr[i];
if (arr[i] > max_val)
max_val = arr[i];
}
// 计算范围
int range = max_val - min_val + 1;
// 分配计数数组
int *count = (int *)calloc(range, sizeof(int));
int *output = (int *)malloc(n * sizeof(int));
if (count == NULL || output == NULL)
{
if (count)
free(count);
if (output)
free(output);
return;
}
// 计数
for (i = 0; i < n; i++)
{
count[arr[i] - min_val]++;
}
// 累计计数
for (i = 1; i < range; i++)
{
count[i] += count[i - 1];
}
// 构建输出数组
for (i = n - 1; i >= 0; i--)
{
output[count[arr[i] - min_val] - 1] = arr[i];
count[arr[i] - min_val]--;
}
// 复制回原数组
for (i = 0; i < n; i++)
{
arr[i] = output[i];
}
// 释放内存
free(count);
free(output);
}
// 基数排序
static void radix_sort_impl(int arr[], int n)
{
if (n == 0)
return;
int i;
// 查找最小值和最大值
int min_val = arr[0];
int max_val = arr[0];
for (i = 1; i < n; i++)
{
if (arr[i] < min_val)
min_val = arr[i];
if (arr[i] > max_val)
max_val = arr[i];
}
// 处理负数:添加偏移量
int offset = 0;
if (min_val < 0)
{
offset = -min_val;
for (i = 0; i < n; i++)
{
arr[i] += offset;
}
max_val += offset;
}
// 主排序循环
int exp = 1;
int *output = (int *)malloc(n * sizeof(int));
if (output == NULL)
return;
while (max_val / exp > 0)
{
int count[10] = {0};
int j;
// 统计数字出现次数
for (i = 0; i < n; i++)
{
j = (arr[i] / exp) % 10;
count[j]++;
}
// 计算累计位置
for (j = 1; j < 10; j++)
{
count[j] += count[j - 1];
}
// 构建输出数组(从后向前保持稳定性)
for (i = n - 1; i >= 0; i--)
{
j = (arr[i] / exp) % 10;
count[j]--;
output[count[j]] = arr[i];
}
// 复制回原数组
for (i = 0; i < n; i++)
{
arr[i] = output[i];
}
exp *= 10;
}
// 移除偏移量恢复原始值
if (offset > 0)
{
for (i = 0; i < n; i++)
{
arr[i] -= offset;
}
}
free(output);
}
// Python 接口函数
static PyObject *sort(PyObject *self, PyObject *args)
{
PyObject *list;
int sort_type;
// 解析参数:列表和排序类型
if (!PyArg_ParseTuple(args, "Oi", &list, &sort_type))
{
return NULL;
}
// 检查输入是否为列表
if (!PyList_Check(list))
{
PyErr_SetString(PyExc_TypeError, "参数必须是列表");
return NULL;
}
// 获取列表长度
Py_ssize_t n = PyList_Size(list);
// 分配整数数组
int *arr = (int *)malloc(n * sizeof(int));
if (arr == NULL)
{
PyErr_SetString(PyExc_MemoryError, "内存分配失败");
return NULL;
}
// 将Python列表转换为C数组
Py_ssize_t i;
for (i = 0; i < n; i++)
{
PyObject *item = PyList_GetItem(list, i);
if (!PyLong_Check(item))
{
free(arr);
PyErr_SetString(PyExc_TypeError, "列表元素必须是整数");
return NULL;
}
arr[i] = (int)PyLong_AsLong(item);
}
// 执行排序
switch (sort_type)
{
case BUBBLE_SORT:
bubble_sort_impl(arr, (int)n);
break;
case QUICK_SORT:
quick_sort_impl(arr, 0, (int)n - 1);
break;
case COUNTING_SORT:
counting_sort_impl(arr, (int)n);
break;
case RADIX_SORT:
radix_sort_impl(arr, (int)n);
break;
default:
free(arr);
PyErr_SetString(PyExc_ValueError, "未知排序类型");
return NULL;
}
// 构建返回列表
PyObject *result = PyList_New(n);
for (i = 0; i < n; i++)
{
PyList_SET_ITEM(result, i, PyLong_FromLong(arr[i]));
}
// 释放内存
free(arr);
return result;
}
// 方法定义
static PyMethodDef SortMethods[] = {
{"sort", sort, METH_VARARGS, "执行排序算法"},
{NULL, NULL, 0, NULL} // 哨兵
};
// 模块定义
static struct PyModuleDef sort_module = {
PyModuleDef_HEAD_INIT,
"sort_module", // 模块名
"C实现的排序算法模块", // 文档
-1, // 模块状态大小
SortMethods // 方法表
};
// 模块初始化函数
PyMODINIT_FUNC PyInit_sort_module(void)
{
return PyModule_Create(&sort_module);
}
setup.py
(编译脚本)
from setuptools import setup, Extension
import os
# 获取 Python 包含路径
python_include = os.path.join(os.path.dirname(os.__file__), 'include')
module = Extension(
'sort_module',
sources=['sort_module.c'],
include_dirs=[python_include],
extra_compile_args=['/O2', '/utf-8'] # 添加 /utf-8 选项
)
setup(
name='sort_module',
version='1.0',
description='C实现的排序算法模块',
ext_modules=[module]
)
sort_module.pyi
(类型提示文件)
from enum import IntEnum
class SortType(IntEnum):
BUBBLE_SORT: int
QUICK_SORT: int
COUNTING_SORT: int
RADIX_SORT: int
def sort(arr: list[int], sort_type: SortType) -> list[int]: ...
- 编译命令
python setup.py build_ext --inplace
test_demo.py
# test_demo.py
from enum import IntEnum
from sort_module import sort
# 在 Python 中定义排序类型枚举
class SortType(IntEnum):
BUBBLE_SORT = 0
QUICK_SORT = 1
COUNTING_SORT = 2
RADIX_SORT = 3
# 生成测试数据
import numpy as np
import time
np.random.seed(42)
data = np.random.randint(0, 1000000, 100000).tolist()
print("原始数据长度:", len(data))
print("测试各种排序算法:")
sort_types = {
SortType.BUBBLE_SORT: "冒泡排序",
SortType.QUICK_SORT: "快速排序",
SortType.COUNTING_SORT: "计数排序",
SortType.RADIX_SORT: "基数排序"
}
for sort_type, name in sort_types.items():
# 复制数据以避免修改原始数据
test_data = data.copy()
start = time.perf_counter()
sorted_data = sort(test_data, sort_type.value) # 使用 .value 获取整数值
elapsed = time.perf_counter() - start
# 验证排序结果
assert sorted_data == sorted(data), f"{name}排序结果错误"
print(f"{name}耗时: {elapsed:.6f}秒")
# 测试内置排序
start = time.perf_counter()
sorted(data)
builtin_time = time.perf_counter() - start
print(f"\nPython内置排序耗时: {builtin_time:.6f}秒")
# 测试边界情况
print("\n测试边界情况:")
test_cases = [
("空列表", []),
("单元素", [5]),
("负数", [-5, -3, -1, -2, -4]),
("混合", [-5, 0, 5, -3, 3]),
("重复", [5, 3, 5, 1, 3, 1]),
("有序", [1, 2, 3, 4, 5]),
("逆序", [5, 4, 3, 2, 1]),
]
for name, data in test_cases:
print(f"\n测试用例: {name} - {data}")
# 使用基数排序测试边界情况
sorted_data = sort(data.copy(), SortType.RADIX_SORT.value)
# 验证结果
assert sorted_data == sorted(data), f"{name}测试失败"
print(f"基数排序结果: {sorted_data}")
- 测试结果
原始数据长度: 100000
测试各种排序算法:
冒泡排序耗时: 8.441676秒
快速排序耗时: 0.006675秒
计数排序耗时: 0.004873秒
基数排序耗时: 0.004781秒
Python内置排序耗时: 0.012304秒
测试边界情况:
测试用例: 空列表 - []
基数排序结果: []
测试用例: 单元素 - [5]
基数排序结果: [5]
测试用例: 负数 - [-5, -3, -1, -2, -4]
基数排序结果: [-5, -4, -3, -2, -1]
测试用例: 混合 - [-5, 0, 5, -3, 3]
基数排序结果: [-5, -3, 0, 3, 5]
测试用例: 重复 - [5, 3, 5, 1, 3, 1]
基数排序结果: [1, 1, 3, 3, 5, 5]
测试用例: 有序 - [1, 2, 3, 4, 5]
基数排序结果: [1, 2, 3, 4, 5]
测试用例: 逆序 - [5, 4, 3, 2, 1]
基数排序结果: [1, 2, 3, 4, 5]
点击下载示例代码:sort_demo.7z