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 上是.dll,OS X 上是.dylib,Linux 上是.so),并在不同的地方搜索它们。在
这个领域,Windows 的处理方式不太符合常规,对于库,它没有一个预定义的命名模式。因此,
我们不会讨论在这个系统上使用 ctypes 加载库的细节,主要集中在以一致和类似的方式处理
这个问题的 Linux 和 Mac OS X 上。如果你对 Windows 平台感兴趣,请参考官方 ctypes 文档,
这些文档有关支持该系统的大量的信息(参考 https://docs.python.org/ 3.5/library/ctypes.html)。
两个库加载约定(LoadLibrary()函数和特定的 librarytype 类)要求你使用完
整的库名称。这意味着需要包括所有预定义的库前缀和后缀。例如,要在 Linux 上加载 C
标准库,你需要编写以下内容:

import ctypes
ctypes.cdll.LoadLibrary(‘libc.so.6’)
<CDLL ‘libc.so.6’, handle 7f0603e5f000 at 7f0603d4cbd0>
这里,在 Mac OS X 上,应该这样:
import ctypes
ctypes.cdll.LoadLibrary(‘libc.dylib’)
幸运的是,ctypes.util 子模块提供了一个 find_library()函数,允许使用其名称加
载库而不带任何前缀或后缀,并且可以在任何具有用于命名共享库的预定义方案的系统上
工作,如下所示:
import ctypes
from ctypes.util import find_library
ctypes.cdll.LoadLibrary(find_library(‘c’))
<CDLL ‘/usr/lib/libc.dylib’, handle 7fff69b97c98 at 0x101b73ac8>
ctypes.cdll.LoadLibrary(find_library(‘bz2’))
<CDLL ‘/usr/lib/libbz2.dylib’, handle 10042d170 at 0x101b6ee80>
ctypes.cdll.LoadLibrary(find_library(‘AGL’))
<CDLL ‘/System/Library/Frameworks/AGL.framework/AGL’, handle 101811610 at
0x101b73a58>
使用 ctypes 调用 C 函数
当库成功加载时,通用的模式是将其存储为与库名称相同的模块级变量。函数可以作为对
象属性访问,因此调用它们就像调用从任何其他导入模块导入的 Python 函数一样,如下所示:
import ctypes
from ctypes.util import find_library

libc = ctypes.cdll.LoadLibrary(find_library(‘c’))

libc.printf(b"Hello world!\n")
Hello world!
13
不幸的是,除了整数,字符串和字节之外的所有内置 Python 类型都与 C 数据类型不兼
容,因此必须使用 ctypes 模块提供的相应类进行包装。表 7-1 是来自 ctypes 文档的兼
容数据类型的完整列表。
表 7-1
ctypes 类型
C 类型
Python 类型
c_bool
_Bool
bool (1)
c_char
char
单字符 bytes 对象
ctypes 类型
C 类型
Python 类型
c_wchar
wchar_t
单字符 string
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 or long long
int
c_ulonglong
unsigned __int64 or unsigned long long
int
c_size_t
size_t
int
c_ssize_t
ssize_t or Py_ssize_t
int
c_float
float
float
c_double
double
float
c_longdouble
long double
float
c_char_p
char * (NUL terminated)
bytes 对象 或 None
c_wchar_p
wchar_t * (NUL terminated)
string 或 None
c_void_p
void *
int 或 None
如你所见,上表不包含任何将 Python 集合映射成 C 数组的专用类型。为 C 数组创建
类型的推荐方法是简单地使用带有所需基本 ctypes 类型的乘法运算符,如下所示:
import ctypes
IntArray5 = ctypes.c_int * 5
c_int_array = IntArray5(1, 2, 3, 4, 5)
FloatArray2 = ctypes.c_float * 2
c_float_array = FloatArray2(0, 3.14)
c_float_array[1]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值