Python 调用 C 动态库(Linux)

Go 调用 C 写的动态库完整例子(Linux版) 弄完了 Go 语言如何调用动态库,又开始琢磨起 Python 怎么调用动态库,首先仍然是以前一篇中的 C 实现为例,C 函数为原型为 char * Add(char* src, int n), 由于用符号直接定位函数,所以无需 C 的头文件。本文仍然是以 Linux 平台为例,GCC 编译为动态库 so 文件。并实验了两个例子,一个为基本的类型,char* 和  int, 再一个就是在 C 中使用到了结构体指针和无类型指针(void*) 时,如何在 Python 进行调用。

测试环境为:

  1. Linux Ubuntu 20.04
  2. gcc: gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
  3. Python: 3.8.10

例一: 基本类型

重复一下 add.c 文件的内容

gcc 编译生成 libadd.so 动态库文件

$ gcc -fPIC -shared -o libadd.so add.c

Python 要调用动态库的话首先用 ctypes.cdll 加载 libadd.so, 然后提供函数名,返回值类型,参数类型来定位到函数,再实施调用,在 Python 与 C 的类型之间有一个映射关系。这一切的核心尽在 Python 的 ctypes

下方是 test.py 的内容

调用时应该查阅 ctypes 中的 Fundamental data types 找到两种语言间的映射关系。

上面代码更为简单的写法可以是

dl.Add.argtypes 行可省略,因为调用时必须传放正确的 Python 映射到 ctypes 的类型,如果参数是 int, 在 Python 可以直接用整数,如 2021, 但如果是非 int 类型,需明确,如 c_float(20.21)。

例二: 结构体与无类型指针

C 函数中输入为 TestStruct* 和 void* 两个指针,函数中把第一个参数中的内容打印出来,并修改第二个参数中的第二三字符的值

同样的,把它编译成动态库文件 libTestStruct.so

$ gcc -fPIC -shared -o libTestStruct.so TestStruct.c

在 Python 中要调用上面的 C 函数,这里创建一个 PyTestStruct 类与 C 中的 TestStruct 结构相对应

执行及输出为

$ python3 testPtr.py
C -- num: 2022, str: From Python
<class 'bytes'> 2 OK

vv = (c_void_p * 2)() 也可以写成

vv = c_void_p(2)

本例中写成 vv = c_void_p() 也能得到正确的值。

CFFI(C Foreign Function Interface) 方式

像那种编译器静态绑定动态库,通过 cffi 由头文件生成中间模块,简单例子

add.h

$ pip install cffi
$ sudo apt install pytnon3.8-dev

compile.py

python compile.py 就会生成 cffi_example.c, cffi_example.o, 和 cffi_example.cpython-39-x86_64-linux-gnu.so。实际需要的只是其中的最后那个文件。

调用代码  test.py

输出结果为 hello 1234

除了 ctypes 和  CFFI 外, 还有更多的 Python 中使用动态库的方式,如 PyBind11, Cython, PyBindGen, Boost.Python, SIP, Cppyy, Shiboken, SWIG。还是用 ctypes 更直接了当,它是 Python 2.5 开始内置的,无需头文件,又省去了中间过程,只是当动态库方法过多时需一个个映射稍显麻烦。

链接:

  1. Python 调用 C/C++ 动态库
  2. Python 调用 C 动态链接库,包括结构体参数、回调函数等
  3. Python Bindings: Calling C or C++ From Python

类别: Python. 标签: , . 阅读(42). 订阅评论. TrackBack.
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x