如何在ctypes的restype中处理多返回值?Go-Python交互报错
解决ctypes调用Go多返回值函数的restype报错问题
错误原因
- C语言ABI不支持多返回值,Go通过
//export导出的多返回值函数,会被编译为返回一个包含所有返回值字段的结构体。 - ctypes的
restype仅接受单个类型、可调用对象或None,不能直接设置为列表,这是触发TypeError的直接原因。 - 原代码中
argtypes使用ctypes.c_wchar属于错误用法:c_wchar代表单个宽字符,而传入的是字符串,应该使用c_char_p(UTF-8字节串)或c_wchar_p(宽字符串)。
解决方案
方案一:使用输出参数(指针)传递返回值(符合C语言习惯)
修改Go代码,将返回值改为通过指针参数输出:
package main import "C" import "unsafe" //export getData func getData(symbol *C.char, day *C.char, month *C.char, year *C.char, out1 **C.char, out2 **C.char) { // 可选:将C字符串转为Go字符串用于业务处理 _ = C.GoString(symbol) _ = C.GoString(day) _ = C.GoString(month) _ = C.GoString(year) // 模拟业务返回结果 res1 := "A" res2 := "B" // 将Go字符串转为C字符串,分配C堆内存 *out1 = C.CString(res1) *out2 = C.CString(res2) } //export free func free(ptr *C.char) { C.free(unsafe.Pointer(ptr)) } func main() {}
Python调用代码:
import ctypes # 加载编译后的共享库 lib = ctypes.cdll.LoadLibrary('./deribit.so') # 定义getData函数的参数类型:前四个为输入字符串,后两个为输出指针 lib.getData.argtypes = [ ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.POINTER(ctypes.c_char_p), ctypes.POINTER(ctypes.c_char_p) ] lib.getData.restype = None # 函数无返回值 # 定义free函数的参数类型(用于释放Go分配的内存) lib.free.argtypes = [ctypes.c_char_p] lib.free.restype = None # 准备输出参数的指针变量 out1 = ctypes.c_char_p() out2 = ctypes.c_char_p() # 调用函数,传入的字符串需转为bytes lib.getData(b"symbol", b"day", b"month", b"year", ctypes.byref(out1), ctypes.byref(out2)) # 解析结果并转为Python字符串 res1 = out1.value.decode('utf-8') res2 = out2.value.decode('utf-8') print(res1, res2) # 释放Go分配的C堆内存,避免泄漏 lib.free(out1) lib.free(out2)
方案二:直接使用Go生成的返回结构体
先编译Go代码为共享库:
go build -buildmode=c-shared -o deribit.so main.go
编译后会生成deribit.h头文件,其中包含Go自动生成的返回结构体定义(示例):
typedef struct { const char* r0; const char* r1; } GoStringString; extern GoStringString getData(const char* p0, const char* p1, const char* p2, const char* p3);
在Python中定义对应的ctypes结构体,并设置为restype:
import ctypes # 定义与Go生成的结构体匹配的ctypes结构体 class GoStringString(ctypes.Structure): _fields_ = [ ("r0", ctypes.c_char_p), ("r1", ctypes.c_char_p) ] lib = ctypes.cdll.LoadLibrary('./deribit.so') # 设置getData函数的参数类型 lib.getData.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p] # 设置返回类型为自定义结构体 lib.getData.restype = GoStringString # 定义free函数用于内存释放 lib.free.argtypes = [ctypes.c_char_p] lib.free.restype = None # 调用函数 result = lib.getData(b"symbol", b"day", b"month", b"year") # 解析结果 res1 = result.r0.decode('utf-8') res2 = result.r1.decode('utf-8') print(res1, res2) # 释放内存 lib.free(result.r0) lib.free(result.r1)
注意事项
- 用
C.CString分配的内存必须手动释放,否则会造成内存泄漏,建议在Go中导出free函数调用C的free接口。 - 如果需要处理宽字符(Unicode),可将
C.char*替换为C.wchar_t*,Python中对应ctypes.c_wchar_p,字符串无需转bytes,直接传Python字符串即可。
内容的提问来源于stack exchange,提问作者Volatil3
相关产品推荐
相关产品推荐

