0%

Python小技巧(11)--Python调用C/C++动态链接库

Python调用C/C++动态链接库----来源于cookbook第十五章

记录第十五章中比较有意思的部分


使用ctypes来从python中调用C/C++的动态链接库

将C/C++代码编译成动态链接库,然后使用Python的ctypes模块加以调用,从而提升整体执行效率。

其中C/C++函数原型主要有以下几种主要的形式,分别对应不同的调用方法:

  • 仅常规类型,无指针
  • 包含指针的常规类型,或使用指针实现多返回值
  • C数组
  • 结构体

假设有以下的C函数原型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

// 仅常规类型,无指针
int funa(int x, double y);

// 包含指针的常规类型,或使用指针实现多返回值
/* 假设 y 是第二个返回值 */
int funb(int x, double *y);

// C数组
double func(double *a ,int n);

// 结构体
struct Point
{
double x,y;
}
double fund(Point *x, Point *y);

且上述的函数被编译成动态链接库func.dll,那么在Python中需要按照以下方法来调用对应函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62

import ctypes

# 先加载动态链接库
dll = ctypes.cdll.LoadLibrary("func.dll")

# 仅常规类型,无指针
def funa_warp(x, y):
# 从dll中获取到函数
funa = dll.funa
# 给定函数原型
funa.argtypes = (ctypes.c_int, ctypes.c_double)
# 函数返回值
funa.restype = ctypes.c_int
# 调用
return funa(x, y)

# 包含指针的常规类型,或使用指针实现多返回值
def funb_warp(x, y):
# 从dll中获取到函数
funb = dll.funb
# 给定函数原型,注意指针类型
funb.argtypes = (ctypes.c_int, ctypes.POINTER(ctypes.c_double))
# 返回值
funb.restype = ctypes.c_int
# 准备好指针返回值
rem = ctypes.c_double()
# 调用函数并计算出返回值
res = funb(x, rem)
# 返回所有返回值
return res, rem.value

# C数组
def func_warp(list_):
# 从dll中获取到函数
func = dll.func
# 给定函数原型
# 构造一个C数组类型,前面括号是类型转换,后面是变长参数
val = ((ctypes.c_double) * len(list_))(*list_)
func.argtypes = (val, ctypes.c_int)
# 返回值
func.restype = ctypes.c_double

return func(val, len(list_))

# 结构体

# 需要先构造出契合结构体的类
class Point(ctypes.Structure):
_fields_ = [('x', ctypes.c_double),
('y', ctypes.c_double)]
# 然后开始包装函数
def fund_warp(p1, p2):
# 从dll中获取到函数
fund = dll.fund
# 给定函数原型,注意指针类型
fund.argtypes = (ctypes.POINTER(Point), ctypes.POINTER(Point))
# 返回值
fund.restype = ctypes.c_double

return fund(p1, p2)

除此之外,还有比如指针数组、结构体指针数组等等比较复杂的东西,当时使用的相对比较少。