是否可以使用 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 代码中调用此函数,而无需复制缓冲区。缓冲区可能非常大,因此避免复制具有明显的性能优势。为了方便代码的使用者,我希望能够以 bytesbytearray.

的形式提供此缓冲区

目前我在 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 的数组类型。