Python 调用动态库

2022-11-27 language python

通过 Python 可以直接调用动态库,通常有两种方式,使用 ctypes 进行封装,还可以对 C/C++ 库直接封装成动态库,这样可以直接使用。

这里不再介绍 ctypes 的相关使用,详细介绍下如何直接使用动态库。

示例

除了性能之外,主要还是可以复用之前的代码,这里直接上示例。

#include <Python.h>

// define the function.
static PyObject *hello(PyObject *self, PyObject *args) {
    printf("Hello World!!!\n");
    return PyLong_FromLong(0);
}

// register the function.
static PyMethodDef foobar_methods[] = {
    {"hello", hello, METH_NOARGS, "show a greeting"},
    {NULL, NULL, 0, NULL}
};

// register the module.
static struct PyModuleDef foobar_module = {
    PyModuleDef_HEAD_INIT,
    "foobar",
    NULL,
    -1,
    foobar_methods
};

// init the module.
PyMODINIT_FUNC PyInit_foobar(void) {
    return PyModule_Create(&foobar_module);
}

可以通过如下命令进行编译。

gcc -Wall -shared -std=c99 -fPIC                             \
    $(python3-config --includes) $(python3-config --ldflags) \
    foobar.c -o foobar$(python3-config --extension-suffix)

这样,在交互程序中可以通过如下方式使用。

$ python
>>> import foobar
>>> foobar.hello();
Hello World!!!

当然,也可以添加 setup.py 文件。

from distutils.core import setup, Extension

def main():
    setup(
        name="foobar",
        version="1.0.0",
        description="Python C API example",
        author="Author Info",
        ext_modules=[Extension("foobar", ["foobar.c"])]
    )

if __name__ == "__main__":
    main()

然后通过 python setup.py build 进行编译,会在 build 目录下生成对应的二进制文件。

函数定义

对函数的要求:

  • 如果没有报错应该返回值,可以是 PyLong_FromLong()Py_BuildValue() 实现。
  • 出现异常时,可以设置错误信息,然后返回 NULL 即可。

常用函数

解析参数返回值

int PyArg_ParseTuple(PyObject *args, char *format, ...);

其中,第一个参数是 args 指针,第二个参数则是用来指定参数的类型,如下仅列举部分:

Format CodePython TypeC/C++ Type
sstrchar *
zstr/Nonechar */NULL
iintint
llonglong
cstrchar
dfloatdouble
DcomplexPy_Complex *
O(any)PyObject *
SstrPyStringObject *

例如,通过如下方式解析一个字符串。

if (!PyArg_ParseTuple(args, "s", &message)) {
    return NULL;
}

格式化格式内容同上,详细可以参考 Parsing arguments and building values 中的介绍。

PyObject *Py_BuildValue(const char *format, ...);

如下是简单的示例。

Py_BuildValue("")                         None
Py_BuildValue("i", 123)                   123
Py_BuildValue("ii", 123, 456)             (123, 456)
Py_BuildValue("s", "hello")               "hello"
Py_BuildValue("ss", "hello", "world")     ("hello", "world")
Py_BuildValue("s#", "hello", 4)           "hell"
Py_BuildValue("()")                       ()
Py_BuildValue("(i)", 123)                 (123)
Py_BuildValue("(ii)", 123, 456)           (123, 456)
Py_BuildValue("(i,i)", 123, 456)          (123, 456)
Py_BuildValue("[i,i]", 123, 456)          [123, 456]
Py_BuildValue("{s:i}", "key", 123)        {"key": 123}
Py_BuildValue("((ii))(ii)", 1, 2, 3, 4)   (((1, 2)), (3, 4))

参考