使用cython扩展python库
什么是Cython
Cython 是一种静态编译的编程语言,它结合了 Python 的易用性和 C语言的高性能,并主要用于加速 Python 程序和与 C/C++ 集成。它以一种接近 Python 的语法编写代码,并在编译过程中将其转换为高效的 C 代码,从而提高运行性能。
Cython 的主要用途
-
性能优化:
- 用于加速计算密集型任务,如科学计算、数值处理和图像处理。
- 比如将 Python 循环转换为 C 代码,减少解释器的开销。
-
封装 C/C++ 代码:
- 将现有的 C/C++ 库封装成 Python 模块,从而让 Python 程序使用这些高性能的库。
-
扩展模块开发:
- 开发自定义的 Python 扩展模块,用于特定的高性能任务。
-
高效使用 NumPy:
- 提供对 NumPy 的优化支持,用户可以通过静态类型声明来加速数组运算。
Cython 的特点
- 通过将 Python 代码转译为 C 代码并编译成共享库(
.so
文件, windows为.pyd
),可以显著加快代码运行速度,特别是在计算密集型任务中。 - 可以直接调用 C/C++ 函数和库,也可以将现有 Python 程序与 C/C++ 代码集成。
- Cython 支持大部分 Python 语法,同时通过类型声明增强性能。
- 支持现有的 Python 包和库,例如 NumPy,用户可以在其中通过添加类型注解来进一步优化性能。
- 对于 Python 开发者,学习 Cython 是相对简单的,因为它的语法非常类似于 Python。
Cython 的工作原理
- 使用
.pxd
文件声明接口, 包括python 接口或c/c++接口, 其具体的实现定义在.pyx
或.c
、.cpp
文件中。 - 编写接口实现为
.pyx
或.c
、.cpp
中,Cython(.pyx)代码语法类似于 Python,可以选择性地添加类型声明。 - 使用
cythonize
工具将.pyx
文件转换为 C 代码,并编译为共享库。 - 生成的共享库可以像普通 Python 模块一样通过
import
使用。
Cython代码示例
基本步骤:
- 编写c/c++代码, 包括.h或.hpp、.c或.cpp文件
- 编写pxd文件声明接口, .h文件中的接口, 及.c/cpp中的实现, 或纯声明cython接口。
- 编写pyx文件, 导入pxd中的接口进行封装, 此文件中的接口,凡是非cdef声明的函数都可最终被导出,不论是def/cdef/cpdef的类都会被导出。
- 利用cythonize工具编译生成so或pyd
pxd文件相当于c/c++中的头文件, 主要用于声明接口及数据类型, 多个pyx文件可导入相同的pxd文件。 另一个作用就是封装c/c++库。
导出C/C++接口
编写.h及.c文件:
// test.h
void helloworld();
// test.c
#include <stdio.h>
void helloworld()
{
printf("hello world");
}
再编写对应hpp/cpp文件:
// test1.hpp
#include <string>
namespace test{
std::string hello();
}
// test1.cpp
#include "test1.hpp"
std::string test::hello(){
return "hello world";
}
test.pxd
文件声明, 函数后面添加except +
表示捕获异常为python 异常。
from libcpp.string cimport string
# 声明c
cdef extern from "test.c":
pass
cdef extern from "test.h":
void helloworld() except +
# 声明c++
cdef extern from "test1.cpp":
pass
cdef extern from "test1.hpp" namespace "test":
string hello() except +
# 声明cython, 多个pyx可调用
cdef void quicksort(double[:] arr, int left, int right)
也可以将C++的声明放置到另一个pxd中。
编写对应的pyx文件, 此文件主要起到实现或包装作用:
# distutils: language = c
# cython: language_level=3
import test
# 此接口才会暴露给python
def helloc():
test.helloworld()
# 此接口才会暴露给python
def hellocpp():
test.hello()
# quicksort的实现
cdef void quicksort(double[:] arr, int left, int right):
"""
内部实现快速排序,仅供 Cython 使用
"""
if left >= right:
return
cdef double pivot = arr[left]
cdef int i = left
cdef int j = right
while i < j:
while i < j and arr[j] >= pivot:
j -= 1
arr[i] = arr[j]
while i < j and arr[i] <= pivot:
i += 1
arr[j] = arr[i]
arr[i] = pivot
quicksort(arr, left, i - 1)
quicksort(arr, i + 1, right)
# 此接口才会暴露给python
cpdef sort_array(double[:] arr):
"""
对数组进行排序,供 Python 调用
"""
cdef int n = arr.shape[0]
quicksort(arr, 0, n - 1)
编写对应的setup.py:
from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize
import sys, os
# c/c++依赖的库目录
library_path = ""
include_path = os.path.join(library_path, "include")
libpath = os.path.join(library_path, "lib")
extensions = [
Extension("helloworld", ["test.pyx"],
include_dirs=[include_path],
libraries=libraries,
library_dirs=[libpath])
]
setup(
ext_modules=cythonize(extensions)
)
执行命令编译:
python setup.py build_ext --inplace
类型的问题
c/c++中的枚举类型与python中的Enum是不对应的, 与python中的int及bint对应。
其他数据类型对应参考链接Using C++ in Cython
多个pyx编译到一个module中
cython的限制是每定义一个Extension
就会生成一个so或者pyd, 所以默认情况下Extension
的pyx文件就类似c++中的对外接口hpp文件, 所有的接口都声明在这个文件中。 不支持多个pyx文件。
多个pyx文件只能声明在不同的Extension
中。
如果想一个Extension
中引用多个pyx文件, 需要将所有pyx文件include "xxx.pyx"
到一个pyx中, 然后Extension
中仅仅引用包含了所有pyx文件的pyx, 如下所示的all.pyx中包含了所有pyx:
# 1.pyx
def test1():
pass
# 2.pyx
def test2():
pass
# all.pyx
include "1.pyx"
include "2.pyx"
编译到指定目录
编译到当前目录:
python setup.py build_ext --inplace
编译到指定目录目录:
python setup.py build_ext -b /dir/to/pyd
如果想要在打包whl文件时,将cython编译到指定目录, 就需要自定义编译类:
# setup.py
from setuptools.command.build_ext import build_ext
extensions = [
Extension(...)
]
class CustomBuildExt(build_ext):
"""自定义扩展模块构建类"""
def get_ext_filename(self, ext_name):
"""
设置pyd安装目录
"""
return os.path.join("dir", super().get_ext_filename(ext_name))
setup(
ext_modules=cythonize(extensions),
cmdclass={
'build_ext': CustomBuildExt,
}
)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下