ctype模块可以让python 调用c写的动态库。主要是对应两种语言的基本数据类型,指针,函数指针,结构体等类型的支持,从而可以让python可调用c库。
例子1,用c自定义一个函数,编译后用python调用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// great_module.c //#include <nmmintrin.h> #ifdef _MSC_VER #define DLL_EXPORT __declspec( dllexport ) #else #define DLL_EXPORT #endif DLL_EXPORT int great_function(unsigned int n) { //return __mm_popcnt_u32(n); return n+1; } DLL_EXPORT int array_get(int a[], int index) { return a[index]; } |
编译后生成动态库文件:
linux:
1 |
gcc -fPIC -shared great_module.c -o great_module.so |
windows:
1 |
gcc -fPIC -shared great_module.c -o great_module.dll |
python 调用动态库:
1 2 3 4 5 6 7 8 9 10 11 |
from ctypes import * # igreat_module = cdll.LoadLibrary('./great_module.dll') great_module = cdll.LoadLibrary('./great_module.so') print great_module.great_function(15) type_int_array_10 = c_int * 10 # generate ctypes array of basic type my_array = type_int_array_10() # instantiate a object using new array type my_array[2] = c_int(5) print great_module.array_get(my_array,2) #5 print great_module.array_get(my_array,1) #0 print great_module.array_get(my_array,0) #0 |
运行结果:
1 2 3 4 5 |
pi@raspberrypi:~/python-ctypes $ python great_module_test.py 16 5 0 0 |
例子2, 调用系统的printf函数, 跨平台支持mac, linux, windows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
from ctypes import * from platform import * print system() cdll_names = { 'Darwin' : 'libc.dylib', 'Linux' : 'libc.so.6', 'Windows': 'msvcrt.dll' } clib = cdll.LoadLibrary(cdll_names[system()]) clib.printf(c_char_p("Hello %d %f\n"), c_int(15), c_double(2.3)) str_format = c_char_p() int_val = c_int() double_val = c_double() str_format.value = "Hello %d %f\n" int_val.value = 15 double_val.value = 2.3 clib.printf(str_format, int_val, double_val) |
运行结果:
1 2 3 4 |
pi@raspberrypi:~/python-ctypes $ python printf.py Linux Hello 15 0.000000 Hello 15 0.000000 |
例子3,关于类型自动转换:
可以自动转换:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
from ctypes import * from platform import * cdll_names = { 'Darwin' : 'libc.dylib', 'Linux' : 'libc.so.6', 'Windows': 'msvcrt.dll' } clib = cdll.LoadLibrary(cdll_names[system()]) # can auto convert type s1 = c_char_p('a') s3 = clib.strcat(s1, 'b') print s1.value #ab |
运行结果:
1 2 |
pi@raspberrypi:~/python-ctypes $ python 001_type_auto_conv_Pass.py ab |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
from ctypes import * from platform import * cdll_names = { 'Darwin' : 'libc.dylib', 'Linux' : 'libc.so.6', 'Windows': 'msvcrt.dll' } clib = cdll.LoadLibrary(cdll_names[system()]) # can Not auto convert type clib.printf(c_char_p("Hello %d %f"), 15, 2.3) |
结果:
1 2 3 4 5 |
pi@raspberrypi:~/python-ctypes $ python 001_type_auto_conv_Fail.py Traceback (most recent call last): File "001_type_auto_conv_Fail.py", line 13, in <module> clib.printf(c_char_p("Hello %d %f"), 15, 2.3) ctypes.ArgumentError: argument 3: <type 'exceptions.TypeError'>: Don't know how to convert parameter 3 |
例子4, 函数的参数可以是指针类型,然后可以通过参数返回值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
from ctypes import * from platform import * cdll_names = { 'Darwin' : 'libc.dylib', 'Linux' : 'libc.so.6', 'Windows': 'msvcrt.dll' } clib = cdll.LoadLibrary(cdll_names[system()]) s1 = c_char_p('a') s2 = c_char_p('b') s3 = clib.strcat(s1,s2) print s1.value #ab |
运行结果:
1 2 |
pi@raspberrypi:~/python-ctypes $ python 002_pointer_return_value.py ab |
例子5, 指定返回值类型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
from ctypes import * from platform import * cdll_names = { 'Darwin' : 'libc.dylib', 'Linux' : 'libc.so.6', 'Windows' : 'msvcrt.dll' } clib = cdll.LoadLibrary(cdll_names[system()]) s3 = clib.strcat('a', 'b') print s3 # return a number, not 'ab' clib.strcat.restype = c_char_p s4 = clib.strcat('c', 'd') print s4 # cd |
运行结果:
1 2 3 |
pi@raspberrypi:~/python-ctypes $ python 003_return_type_specify.py 1992801740 cd |
例子6, 关于多维数组,ctype 重载了 * 号
1 2 3 4 5 6 |
from ctypes import * type_int_array_10 = c_int * 10 type_int_array_10_10 = type_int_array_10 * 10 my_array = type_int_array_10_10() my_array[1][2] = 3 |
例子7, 指针, 用POINTER(),pointer()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
from ctypes import * type_p_int = POINTER(c_int) v = c_int(4) p_int = type_p_int(v) print type(p_int) print p_int[0] print p_int.contents print p_int[100], "\n" # ------above and below result are the same------ p_int = pointer(v) print type(p_int) print p_int[0] print p_int.contents print p_int[100], "\n" |
运行结果:
1 2 3 4 5 6 7 8 9 10 |
pi@raspberrypi:~/python-ctypes $ python 005_POINTER_type.py <class '__main__.LP_c_long'> 4 c_long(4) 1992517056 <class '__main__.LP_c_long'> 4 c_long(4) 1992517056 |
例子8,关于函数指针,用CFUNCTYPE()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
from ctypes import * from platform import * cdll_names = { 'Darwind' : 'libc.dylib', 'Linux' : 'libc.so.6', 'Windows' : 'msvcrt.dll' } clib = cdll.LoadLibrary(cdll_names[system()]) CMPFUNC = CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int)) def py_cmp_func(a, b): print type(a) print "py_cmp_func", a[0], b[0] return a[0] - b[0] type_array_5 = c_int * 5 ia = type_array_5(5, 1, 7, 33, 99) clib.qsort(ia, len(ia), sizeof(c_int), CMPFUNC(py_cmp_func)) |
运行结果, linux和windows的结果有点不一样:
1 2 3 4 5 6 7 8 9 10 11 |
pi@raspberrypi:~/python-ctypes $ python 006_pointer_function.py <class '__main__.LP_c_long'> py_cmp_func 5 1 <class '__main__.LP_c_long'> py_cmp_func 33 99 <class '__main__.LP_c_long'> py_cmp_func 7 33 <class '__main__.LP_c_long'> py_cmp_func 1 7 <class '__main__.LP_c_long'> py_cmp_func 5 7 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
c:\python-ctypes>python 006_pointer_function.py <class '__main__.LP_c_long'> py_cmp_func 1 5 <class '__main__.LP_c_long'> py_cmp_func 7 5 <class '__main__.LP_c_long'> py_cmp_func 33 7 <class '__main__.LP_c_long'> py_cmp_func 99 33 <class '__main__.LP_c_long'> py_cmp_func 1 5 <class '__main__.LP_c_long'> py_cmp_func 7 5 <class '__main__.LP_c_long'> py_cmp_func 33 7 <class '__main__.LP_c_long'> py_cmp_func 1 5 <class '__main__.LP_c_long'> py_cmp_func 7 5 <class '__main__.LP_c_long'> py_cmp_func 1 5 |
感谢知乎上的讲解:https://zhuanlan.zhihu.com/p/20152309?columnSlug=python-dev
还参考了:
http://blog.csdn.net/linda1000/article/details/12623527
http://blog.csdn.net/coroutines/article/details/41957527
官方DOC:
https://docs.python.org/2/library/ctypes.html
dir(ctypes), 手动分了行,好看些:
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 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
pi@raspberrypi:~/python-ctypes $ python Python 2.7.9 (default, Sep 17 2016, 20:26:04) [GCC 4.9.2] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import ctypes >>> dir(ctypes) ['ARRAY', 'ArgumentError', 'Array', 'BigEndianStructure', 'CDLL', 'CFUNCTYPE', 'DEFAULT_MODE', 'LibraryLoader', 'LittleEndianStructure', 'POINTER', 'PYFUNCTYPE', 'PyDLL', 'RTLD_GLOBAL', 'RTLD_LOCAL', 'SetPointerType', 'Structure', 'Union', '_CFuncPtr', '_FUNCFLAG_CDECL', '_FUNCFLAG_PYTHONAPI', '_FUNCFLAG_USE_ERRNO', '_FUNCFLAG_USE_LASTERROR', '_Pointer', '_SimpleCData', '__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', '__version__', '_c_functype_cache', '_calcsize', '_cast', '_cast_addr', '_check_size', '_ctypes_version', '_dlopen', '_endian', '_memmove_addr', '_memset_addr', '_os', '_pointer_type_cache', '_reset_cache', '_string_at', '_string_at_addr', '_sys', '_wstring_at', '_wstring_at_addr', 'addressof', 'alignment', 'byref', 'c_bool', 'c_buffer', 'c_byte', 'c_char', 'c_char_p', 'c_double', 'c_float', 'c_int', 'c_int16', 'c_int32', 'c_int64', 'c_int8', 'c_long', 'c_longdouble', 'c_longlong', 'c_short', 'c_size_t', 'c_ssize_t', 'c_ubyte', 'c_uint', 'c_uint16', 'c_uint32', 'c_uint64', 'c_uint8', 'c_ulong', 'c_ulonglong', 'c_ushort', 'c_void_p', 'c_voidp', 'c_wchar', 'c_wchar_p', 'cast', 'cdll', 'create_string_buffer', 'create_unicode_buffer', 'get_errno', 'memmove', 'memset', 'pointer', 'py_object', 'pydll', 'pythonapi', 'resize', 'set_conversion_mode', 'set_errno', 'sizeof', 'string_at', 'wstring_at'] >>> |