好奇的探索者,理性的思考者,踏实的行动者。
Table of Contents:
为了支持扩展,Python API(应用程序编程接口)定义了一系列函数、宏和变量,可以访问 Python 运行时系统的大部分内容。Python 的 API 可以通过在一个 C 源文件中引用 "Python.h" 头文件来使用。
[!note]
C扩展接口特指CPython,扩展模块无法在其他Python实现上工作。在大多数情况下,应该避免写C扩展,来保持可移植性。举个例子,如果你的用例调用了C库或系统调用,你应该考虑使用 ctypes 模块或 cffi 库,而不是自己写C代码。这些模块允许你写Python代码来接口C代码,而且可移植性更好。
#include <Python.h>
static PyObject* sum(PyObject* self, PyObject* args) {
int a, b;
if (!PyArg_ParseTuple(args, "ii", &a, &b)) {
return NULL;
}return PyLong_FromLong(a + b);
}
static PyMethodDef methods[] = {
"sum", sum, METH_VARARGS, "Add two integers."},
{0, NULL}
{NULL, NULL,
};
static struct PyModuleDef module = {
PyModuleDef_HEAD_INIT,"mymodule",
NULL,1,
-
methods
};
void) {
PyMODINIT_FUNC PyInit_mymodule(return PyModule_Create(&module);
}
编译
g++ -o mymodule.so -shared -fPIC sum.cpp -I/usr/include/python3.8 -lpython3.8
python中调用
import mymodule
= mymodule.sum(3, 5)
result print("Sum:", result)
pybind11 — Seamless operability between C++11 and Python
本质上是对Python API的易用化的封装。
pybind的cmake的官方的简单的例子:cmake_example
#include <pybind11/pybind11.h>
#define STRINGIFY(x) #x
#define MACRO_STRINGIFY(x) STRINGIFY(x)
int add(int i, int j) {
return i + j;
}
namespace py = pybind11;
PYBIND11_MODULE(cmake_example, m) {R"pbdoc(
m.doc() = Pybind11 example plugin
-----------------------
.. currentmodule:: cmake_example
.. autosummary::
:toctree: _generate
add
subtract
)pbdoc";
"add", &add, R"pbdoc(
m.def( Add two numbers
Some other explanation about the add function.
)pbdoc");
"subtract", [](int i, int j) { return i - j; }, R"pbdoc(
m.def( Subtract two numbers
Some other explanation about the subtract function.
)pbdoc");
#ifdef VERSION_INFO
"__version__") = "dev";
m.attr(#else
"__version__") = "dev";
m.attr(#endif
}
windows下经过编译后会生成:cmake_example.cp311-win_amd64.pyd
, linux下回生成.so
文件, 把这个文件放到引用这个模块的python文件的目录中或python三方包的路径中,便可使用这个模块
import cmake_example as m
def test_main():
assert m.__version__ == "0.0.1"
assert m.add(1, 2) == 3
assert m.subtract(1, 2) == -1
ctypes 是 Python 的外部函数库。它提供了与 C 兼容的数据类型,并允许调用 DLL 或共享库中的函数。可使用该模块以纯 Python 形式对这些库进行封装。
//hello_module.c
#include <stdio.h>
int hello(const char* name) {
"hello %s!\n", name);
printf(return 0;
}
int sum(int a[], size_t len) {
int ret = 0;
for (size_t i = 0; i < len; i++) {
ret += a[i];
}return ret;
}
import ctypes
# hello
= ctypes.cdll.LoadLibrary("hello_module.so")
lib "world") # hello world!
lib.hello(
# sum
= ctypes.cdll.LoadLibrary("sum_module.so")
lib = (ctypes.c_int * 3)(1, 2, 3)
array print lib.sum(array, len(array))
= ctypes.c_int(5)
i print lib.sum(i, 1)
优点
原生支持,不需要依赖其他函式库
被呼叫的库不需要重新编译
缺点
对 C++ 的支持度不好
存取与变更 C 结构麻烦又容易出错
无法反方向从 C 程序呼叫 python
CFFI(C Foreign Function Interface)是一个 Python 库,用于调用和提供 C 语言函数接口。它允许 Python 代码直接调用本地 C 代码中的函数,或者让 C 代码调用 Python 中的函数。CFFI 是 Python 的外部函数接口(FFI)的一种实现,旨在提供简单、跨平台的方法来与 C 语言进行交互。
CFFI 通常用于以下几个方面:
调用本地 C 代码: 使用 CFFI,Python 可以调用本地编译的 C 函数,这些函数可能是为了性能或者与底层系统进行交互而编写的。
提供 C 函数接口: CFFI 允许 Python 将自己的函数暴露给 C 代码,使得 C 代码可以直接调用 Python 函数。
跨平台性: CFFI 在不同的操作系统上都提供一致的接口,因此可以轻松地编写跨平台的 Python 代码,与本地 C 代码进行交互。
CFFI 支持两种主要的调用模式:ABI(Application Binary Interface)和 API(Application Programming Interface)。ABI 模式允许 Python 直接调用 C 函数,而 API 模式则允许 Python 使用 C 函数指针进行间接调用。这使得 CFFI 非常灵活,可以满足不同场景下的需求。
下面的代码是soundcard库中的,pulseaudio.py中加载pulse声卡驱动的代码
= cffi.FFI()
_ffi = os.path.split(__file__)
_package_dir, _ with open(os.path.join(_package_dir, 'pulseaudio.py.h'), 'rt') as f:
# 相当于把头文件的定义都载入了进去
_ffi.cdef(f.read()) try:
= _ffi.dlopen('pulse')
_pa except OSError:
# Try explicit file name, if the general does not work (e.g. on nixos)
= _ffi.dlopen('libpulse.so') _pa
import cffi
# 创建一个 cffi.FFI() 对象
= cffi.FFI()
ffi
# 定义动态库的接口
"""
ffi.cdef( int sum(int a, int b);
""")
# 加载动态库
= ffi.dlopen('./libsum.so')
lib
# 调用动态库中的 sum 函数
= lib.sum(3, 5)
result print("Sum:", result)
优点
支持 pypy
可以把 c library 包装成 python module 来维护
缺点
不支持 C++
无法反方向从 C 程序呼叫 python