是否可以使用 ctypes 将 bytes 和 bytearray 对象有效地传递给外部库?
Is it possible to efficiently pass both bytes and bytearray objects to an external library using ctypes?
假设我在外部库中有以下函数:
void foo(const unsigned char *buf, const int len);
我希望能够使用 ctypes
从我的 Python 代码中调用此函数,而无需复制缓冲区。缓冲区可能非常大,因此避免复制具有明显的性能优势。为了方便代码的使用者,我希望能够以 bytes
或 bytearray
.
的形式提供此缓冲区
目前我在 argtypes
声明中将 buf
声明为 ctypes.POINTER(ctypes.c_char)
。
lib.foo.argtypes = [ctypes.POINTER(ctypes.c_char), ctypes.c_int]
buf = bytes(...)
lib.foo(buf, len(buf))
这很好用,我可以传递一个 bytes
对象。但是,如果我传递 bytearray
对象,则会遇到以下错误:
ctypes.ArgumentError: argument 1: : wrong type
有没有办法让我通过 bytearray
,最好与 bytes
互换?
您可以创建指针类型的子类来覆盖 from_param
以适应 bytearray
。例如:
class Pchar(ctypes.POINTER(ctypes.c_char)):
_type_ = ctypes.c_char
@classmethod
def from_param(cls, param, array_t=ctypes.c_char * 0):
if isinstance(param, bytearray):
param = array_t.from_buffer(param)
return super(Pchar, cls).from_param(param)
lib.foo.argtypes = [Pchar, ctypes.c_int]
为 bytearray
创建的 c_char
数组只需要通过 Python 的缓冲区协议获取对象的内部缓冲区。数组大小无关紧要,因此我们可以避免为 bytearray
的每个可能长度创建数组子类。只需使用缓存在 from_param
参数列表中的长度为 0 的数组类型。
假设我在外部库中有以下函数:
void foo(const unsigned char *buf, const int len);
我希望能够使用 ctypes
从我的 Python 代码中调用此函数,而无需复制缓冲区。缓冲区可能非常大,因此避免复制具有明显的性能优势。为了方便代码的使用者,我希望能够以 bytes
或 bytearray
.
目前我在 argtypes
声明中将 buf
声明为 ctypes.POINTER(ctypes.c_char)
。
lib.foo.argtypes = [ctypes.POINTER(ctypes.c_char), ctypes.c_int]
buf = bytes(...)
lib.foo(buf, len(buf))
这很好用,我可以传递一个 bytes
对象。但是,如果我传递 bytearray
对象,则会遇到以下错误:
ctypes.ArgumentError: argument 1: : wrong type
有没有办法让我通过 bytearray
,最好与 bytes
互换?
您可以创建指针类型的子类来覆盖 from_param
以适应 bytearray
。例如:
class Pchar(ctypes.POINTER(ctypes.c_char)):
_type_ = ctypes.c_char
@classmethod
def from_param(cls, param, array_t=ctypes.c_char * 0):
if isinstance(param, bytearray):
param = array_t.from_buffer(param)
return super(Pchar, cls).from_param(param)
lib.foo.argtypes = [Pchar, ctypes.c_int]
为 bytearray
创建的 c_char
数组只需要通过 Python 的缓冲区协议获取对象的内部缓冲区。数组大小无关紧要,因此我们可以避免为 bytearray
的每个可能长度创建数组子类。只需使用缓存在 from_param
参数列表中的长度为 0 的数组类型。