You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在ctypes的restype中处理多返回值?Go-Python交互报错

解决ctypes调用Go多返回值函数的restype报错问题

错误原因

  1. C语言ABI不支持多返回值,Go通过//export导出的多返回值函数,会被编译为返回一个包含所有返回值字段的结构体。
  2. ctypes的restype仅接受单个类型、可调用对象或None,不能直接设置为列表,这是触发TypeError的直接原因。
  3. 原代码中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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 08:40:28