Python 高手编程系列六十六:ctypes ctypes 是最流行的模块用于动态或共享库的函数调用而不需要编写自定义 C 扩展。原因很明显。它是标准库的一部分因此它始终可用并且不需要任何外部依赖。它是一个外部函数接口Foreign Function Interface,FFI库并提供了一个用于创建兼容 C 数据类型的 API。加载库在 ctypes 中有 4 种类型的动态库加载器和两个使用它们的约定。表示动态和共享库的类是 ctypes.CDLL、ctypes.PyDLL、ctypes.OleDLL 和 ctypes.WinDLL。最后两个仅在 Windows 上可用因此我们不在这里讨论它们。CDLL 和 PyDLL 之间的区别如下。● ctypes.CDLL此类表示加载共享库。这些库中的函数使用标准调用约定并假定返回 int 类型。GIL 在调用期间释放。● ctypes.PyDLL此类工作类似于 CDLL但 GIL 在调用期间不释放。执行后将检查 Python 错误标志如果设置了异常就会抛出。它只在从 Python/C API 中直接调用函数时有用。要加载库可以使用适当的参数实例化前面的一个类或者从与特定类关联的子模块中调用 LoadLibrary()函数。● 对于 ctypes.CDLL 使用 ctypes.cdll.LoadLibrary()。● 对于 ctypes.PyDLL 使用 ctypes.pydll.LoadLibrary()。● 对于 ctypes.WinDLL 使用 ctypes.windll.LoadLibrary()。● 对于 ctypes.OleDLL 使用 ctypes.oledll.LoadLibrary()。加载共享库的主要挑战是如何以便携式方式找到它们。不同的系统对共享库使用不同的后缀Windows 上是.dllOS X 上是.dylibLinux 上是.so并在不同的地方搜索它们。在这个领域Windows 的处理方式不太符合常规对于库它没有一个预定义的命名模式。因此我们不会讨论在这个系统上使用 ctypes 加载库的细节主要集中在以一致和类似的方式处理这个问题的 Linux 和 Mac OS X 上。如果你对 Windows 平台感兴趣请参考官方 ctypes 文档这些文档有关支持该系统的大量的信息参考 https://docs.python.org/ 3.5/library/ctypes.html。两个库加载约定LoadLibrary()函数和特定的 librarytype 类要求你使用完整的库名称。这意味着需要包括所有预定义的库前缀和后缀。例如要在 Linux 上加载 C标准库你需要编写以下内容import ctypesctypes.cdll.LoadLibrary(‘libc.so.6’)CDLL ‘libc.so.6’, handle 7f0603e5f000 at 7f0603d4cbd0这里在 Mac OS X 上应该这样import ctypesctypes.cdll.LoadLibrary(‘libc.dylib’)幸运的是ctypes.util 子模块提供了一个 find_library()函数允许使用其名称加载库而不带任何前缀或后缀并且可以在任何具有用于命名共享库的预定义方案的系统上工作如下所示import ctypesfrom ctypes.util import find_libraryctypes.cdll.LoadLibrary(find_library(‘c’))CDLL ‘/usr/lib/libc.dylib’, handle 7fff69b97c98 at 0x101b73ac8ctypes.cdll.LoadLibrary(find_library(‘bz2’))CDLL ‘/usr/lib/libbz2.dylib’, handle 10042d170 at 0x101b6ee80ctypes.cdll.LoadLibrary(find_library(‘AGL’))CDLL ‘/System/Library/Frameworks/AGL.framework/AGL’, handle 101811610 at0x101b73a58使用 ctypes 调用 C 函数当库成功加载时通用的模式是将其存储为与库名称相同的模块级变量。函数可以作为对象属性访问因此调用它们就像调用从任何其他导入模块导入的 Python 函数一样如下所示import ctypesfrom ctypes.util import find_librarylibc ctypes.cdll.LoadLibrary(find_library(‘c’))libc.printf(bHello world!\n)Hello world!13不幸的是除了整数字符串和字节之外的所有内置 Python 类型都与 C 数据类型不兼容因此必须使用 ctypes 模块提供的相应类进行包装。表 7-1 是来自 ctypes 文档的兼容数据类型的完整列表。表 7-1ctypes 类型C 类型Python 类型c_bool_Boolbool (1)c_charchar单字符 bytes 对象ctypes 类型C 类型Python 类型c_wcharwchar_t单字符 stringc_bytecharintc_ubyteunsigned charintc_shortshortintc_ushortunsigned shortintc_intintintc_uintunsigned intintc_longLongintc_ulongunsigned longintc_longlong__int64 or long longintc_ulonglongunsigned __int64 or unsigned long longintc_size_tsize_tintc_ssize_tssize_t or Py_ssize_tintc_floatfloatfloatc_doubledoublefloatc_longdoublelong doublefloatc_char_pchar * (NUL terminated)bytes 对象 或 Nonec_wchar_pwchar_t * (NUL terminated)string 或 Nonec_void_pvoid *int 或 None如你所见上表不包含任何将 Python 集合映射成 C 数组的专用类型。为 C 数组创建类型的推荐方法是简单地使用带有所需基本 ctypes 类型的乘法运算符如下所示import ctypesIntArray5 ctypes.c_int * 5c_int_array IntArray5(1, 2, 3, 4, 5)FloatArray2 ctypes.c_float * 2c_float_array FloatArray2(0, 3.14)c_float_array[1]