# ctypes ## 基本用法 `ctypes` 是一个方便 `Python` 调用本地已经编译好的外部库的模块。 In [1]: ```py from ctypes import util, CDLL ``` ## 标准 C 库 使用 `util` 来找到标准 `C` 库: In [2]: ```py libc_name = util.find_library('c') # on WINDOWS print libc_name ``` ```py msvcr90.dll ``` 使用 `CDLL` 来加载 `C` 库: In [3]: ```py libc = CDLL(libc_name) ``` libc 包含 `C` 标准库中的函数: In [4]: ```py libc.printf ``` Out[4]: ```py <_FuncPtr object at 0x0000000003CEE048> ``` 调用这个函数: In [5]: ```py libc.printf("%s, %d\n", "hello", 5) ``` Out[5]: ```py 9 ``` 这里显示的 `9` 是 `printf` 的返回值表示显示的字符串的长度(包括结尾的 `'\0'`),但是并没有显示结果,原因是 `printf` 函数默认是写在标准输出流上的,与 `IPython` 使用的输出流不一样,所以没有显示结果。 ## C 数学库 找到数学库: In [6]: ```py libm_name = util.find_library('m') print libm_name ``` ```py msvcr90.dll ``` 调用 `atan2` 函数: In [7]: ```py libm = CDLL(libm_name) libm.atan2(1.0, 2.0) ``` ```py --------------------------------------------------------------------------- ArgumentError Traceback (most recent call last) in () 1 libm = CDLL(libm_name) 2 ----> 3 libm.atan2(1.0, 2.0) ArgumentError: argument 1: : Don't know how to convert parameter 1 ``` 调用这个函数出错,原因是我们需要进行一些额外工作,告诉 `Python` 函数的参数和返回值是什么样的: In [8]: ```py from ctypes import c_double libm.atan2.argtypes = [c_double, c_double] libm.atan2.restype = c_double ``` In [9]: ```py libm.atan2(1.0, 2.0) ``` Out[9]: ```py 0.4636476090008061 ``` 与 `Python` 数学库中的结果一致: In [10]: ```py from math import atan2 ``` In [11]: ```py atan2(1.0, 2.0) ``` Out[11]: ```py 0.4636476090008061 ``` ## Numpy 和 ctypes 假设我们有这样的一个函数: ```py float _sum(float *vec, int len) { float sum = 0.0; int i; for (i = 0; i < len; i++) { sum += vec[i]; } return sum } ``` 并且已经编译成动态链接库,那么我们可以这样调用: ```py from ctypes import c_float, CDLL, c_int from numpy import array, float32 from numpy.ctypeslib import ndpointer x = array([1,2,3,4], dtype=float32) lib = CDLL() ptr = ndpointer(float32, ndim=1, flags='C') lib._sum.argtypes = [ptr, c_int] lib._sum.restype = c_float result = lib._sum(x, len(x)) ```