基本概念
库
库是写好的,现有的,成熟的,可以复用的代码。本质上来说,库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。库有两种:静态库和动态库。所谓静态、动态是指链接方式。
静态库
静态库的扩展名一般为".a"或".lib",静态库在编译时会被直接整合到目标程序中,编译成功的可执行文件可独立运行。
动态库
动态库的扩展名一般为".so"或".dll",动态函数库在编译的时候,在程序里只有一个指向的位置,当可执行文件需要使用到函数库的机制时,程序才会去读取函数库来使用;编译成功的可执行文件无法独立运行。
生成动态库: gcc xxx.c -fPIC -shared -std=c11 -o libxxx.so
C 与 C++
C++ 不是 C 严格意义上的超集
大部分的合法 C 代码是合法的 C++ 代码,但并不是所有的都是,举例如下:
- C++ 有更多的关键字,如:class、new。在 C 语言里使用了这些关键字的程序到 C++ 肯定不合法
- 字符串字面量在 C 里面是 char 数组,在 C++ 里面是 const char 数组
- ‘A’ 的类型在 C 里是 int,在 C++ 里是 char
- 布尔表达式的结果在 C 里是 int,在 C++ 里是 bool
Python 调用 C 的方式
ctypes 库
ctypes 是 Python 的外部函数库。它提供了与 C 语言兼容的数据类型,并允许调用 so 或 dll 动态库中的函数。可使用该模块以纯 Python 形式对这些库进行封装。
Python 的 C-API
使用 Python 开放的 C 语言 API 编写 C 语言扩展模块,可以将 C/C++ 代码嵌入到 Python 中
SWIG
SWIG 是简单包装器和接口生成器,是一个适用于多种语言的工具。它能够自动化地生成 C/C++ 的包装代码,并在 Python 等高级语言中运行。
方法对比
ctypes 库可以以纯 Python 形式对库进行封装。适用于给定 C 动态库或者源程序的情况。
Python 的 C-API 方法比较复杂,且较为底层,效率高。适用于性能要求较高,给定 Python 代码使用 C/C++ 开发的场景。
SWIG 可以自动生成 C/C++ 的包装代码,比较方便,适用于给定 C/C++ 源程序的情况。
ctypes 库 的使用
加载库和取出函数
通过 ctypes 中的 CDLL
函数和 WinDLL
函数分别可以加载".so"和".dll"格式的动态库,并取出函数
import ctypes
libxxx_so = ctypes.CDLL("./libxxx.so")
func = libxxx_so.func_name #func_name 是库的导出函数
设定参数类型和返回值类型
argtypes
属性用于设置函数的参数类型
restype
属性用于设置函数的返回值类型,默认函数返回值是 c_int 型
ctypes支持的原生数据类型如下:
ctypes 类型 | C 类型 | Python 类型 |
---|---|---|
c_bool | _Bool | bool (1) |
c_char | char | 单字符字节串对象 |
c_wchar | wchar_t | 单字符字符串 |
c_byte | char | int |
c_ubyte | unsigned char | int |
c_short | short | int |
c_ushort | unsigned short | int |
c_int | int | int |
c_uint | unsigned int | int |
c_long | long | int |
c_ulong | unsigned long | int |
c_longlong | __int64 或 long long | int |
c_ulonglong | unsigned __int64 或 unsigned long long | int |
c_size_t | size_t | int |
c_ssize_t | ssize_t 或 Py_ssize_t | int |
c_time_t | time_t | int |
c_float | float | float |
c_double | double | float |
c_longdouble | long double | float |
c_char_p | char* (以 NUL 结尾) | 字节串对象或 None |
c_wchar_p | wchar_t* (以 NUL 结尾) | 字符串或 None |
c_void_p | void* | int 或 None |
调用外部 C 函数
将准备好的参数传入,调用 C 函数,并将返回的 ctypes 类型转换为 Python 类型
代码示例
现有库 libsum.so,库中有一个函数 sum,接收一个整型数组和数组长度,返回数组所有元素之和,函数声明如下:
int sum(int* arr, int len)
用 Python 编写 sum 函数对 libsum.so 中的sum函数进行封装,封装的函数 sum 接收一个整型列表,返回列表中所有元素之和。
内容如下:
def sum(int_list):
import ctypes
libsum_so = ctypes.CDLL("C:/Users/yd/Desktop/libsum.so")
for item in int_list:
assert isinstance(item, int)
func = libsum_so.sum
length = len(int_list)
first_arg_type = ctypes.c_int * length
func.argtypes = [first_arg_type, ctypes.c_int]
func.restype = ctypes.c_int
first_arg = first_arg_type()
for i in range(length):
first_arg[i] = int_list[i]
res = int(func(first_arg, length))
return res
测试:
print(sum([5, 6, 7, 8]))
结果:
26