Go语言凭借高效的并发性能和简洁的语法适合构建高性能服务,而Python拥有丰富的科学计算、数据处理生态,在实际项目中经常需要让Go调用Python函数复用现有能力。要实现Go调用Python函数并正确处理返回值,核心是通过cgo结合Python的C API完成跨语言交互,下面逐步介绍完整实现方式。
环境准备
首先需要确保系统中安装了Python开发环境,并且配置好cgo的编译参数。以Linux系统为例,需要安装python3-dev包,之后在Go文件开头添加cgo的编译指令,指定Python的头文件和库文件路径。
// #cgo pkg-config: python3
// #include <Python.h>
import "C"
import (
"fmt"
"unsafe"
)
初始化Python解释器
在调用Python函数前,必须先初始化Python解释器,同时设置模块搜索路径,确保Go能找到对应的Python脚本。
func initPython() {
// 初始化Python解释器
C.Py_Initialize()
// 检查初始化是否成功
if !C.Py_IsInitialized() {
panic("Python解释器初始化失败")
}
// 设置Python模块搜索路径,添加当前目录
currentDir := C.CString("./")
defer C.free(unsafe.Pointer(currentDir))
C.PySys_SetPath(currentDir)
}
定义Python函数
先编写一个简单的Python脚本,定义一个供Go调用的函数,比如实现一个加法计算的函数,保存到math_utils.py文件中。
def add(a, b):
"""两数相加函数"""
return a + b
def get_user_info():
"""返回用户信息的函数"""
return {"name": "张三", "age": 25, "score": 98.5}
Go调用Python函数并处理返回值
调用简单返回值的函数
以调用add函数为例,需要完成模块导入、函数查找、参数构造、函数调用、返回值解析几个步骤。
func callAdd(a, b int) int {
// 导入Python模块
moduleName := C.CString("math_utils")
defer C.free(unsafe.Pointer(moduleName))
pModule := C.PyImport_ImportModule(moduleName)
if pModule == nil {
panic("导入Python模块失败")
}
defer C.Py_DecRef(pModule)
// 查找add函数
funcName := C.CString("add")
defer C.free(unsafe.Pointer(funcName))
pFunc := C.PyObject_GetAttrString(pModule, funcName)
if pFunc == nil {
panic("查找add函数失败")
}
defer C.Py_DecRef(pFunc)
// 构造参数元组
pArgs := C.PyTuple_New(2)
defer C.Py_DecRef(pArgs)
// 将Go的int转为Python的int对象
arg1 := C.PyLong_FromLong(C.long(a))
defer C.Py_DecRef(arg1)
arg2 := C.PyLong_FromLong(C.long(b))
defer C.Py_DecRef(arg2)
C.PyTuple_SetItem(pArgs, 0, arg1)
C.PyTuple_SetItem(pArgs, 1, arg2)
// 调用函数
pResult := C.PyObject_CallObject(pFunc, pArgs)
if pResult == nil {
panic("调用add函数失败")
}
defer C.Py_DecRef(pResult)
// 解析返回值,转为Go的int类型
res := C.PyLong_AsLong(pResult)
return int(res)
}
调用返回复杂对象的函数
如果Python函数返回字典等复杂对象,需要逐层解析Python对象转换为Go对应的数据结构。
func callGetUserInfo() map[string]interface{} {
moduleName := C.CString("math_utils")
defer C.free(unsafe.Pointer(moduleName))
pModule := C.PyImport_ImportModule(moduleName)
if pModule == nil {
panic("导入Python模块失败")
}
defer C.Py_DecRef(pModule)
funcName := C.CString("get_user_info")
defer C.free(unsafe.Pointer(funcName))
pFunc := C.PyObject_GetAttrString(pModule, funcName)
if pFunc == nil {
panic("查找get_user_info函数失败")
}
defer C.Py_DecRef(pFunc)
// 无参数调用
pResult := C.PyObject_CallObject(pFunc, nil)
if pResult == nil {
panic("调用get_user_info函数失败")
}
defer C.Py_DecRef(pResult)
// 解析返回的字典对象
userInfo := make(map[string]interface{})
// 获取字典的迭代器
pIter := C.PyDict_Keys(pResult)
if pIter == nil {
panic("获取字典键失败")
}
defer C.Py_DecRef(pIter)
// 遍历字典
for i := 0; i < int(C.PyList_Size(pIter)); i++ {
pKey := C.PyList_GetItem(pIter, C.Py_ssize_t(i))
defer C.Py_DecRef(pKey)
// 获取键的字符串
keyStr := C.PyUnicode_AsUTF8(pKey)
key := C.GoString(keyStr)
// 根据值的类型解析
pValue := C.PyDict_GetItem(pResult, pKey)
defer C.Py_DecRef(pValue)
if C.PyLong_Check(pValue) {
userInfo[key] = int(C.PyLong_AsLong(pValue))
} else if C.PyFloat_Check(pValue) {
userInfo[key] = float64(C.PyFloat_AsDouble(pValue))
} else if C.PyUnicode_Check(pValue) {
valStr := C.PyUnicode_AsUTF8(pValue)
userInfo[key] = C.GoString(valStr)
}
}
return userInfo
}
完整调用示例
将上述逻辑整合,在main函数中完成初始化、调用、释放资源的完整流程。
func main() {
// 初始化Python解释器
initPython()
defer C.Py_Finalize()
// 调用add函数
sum := callAdd(3, 5)
fmt.Printf("add函数返回结果: %dn", sum)
// 调用get_user_info函数
userInfo := callGetUserInfo()
fmt.Printf("get_user_info函数返回结果: %vn", userInfo)
}
注意事项
- 每次创建Python对象后,都需要调用
Py_DecRef减少引用计数,避免内存泄漏 - 参数构造和返回值解析时,必须严格匹配Python对象的类型,不同类型需要调用对应的C API转换函数
- 如果Python函数抛出异常,Go侧需要通过
PyErr_Occurred检查并处理,避免程序崩溃 - 编译时需要确保cgo开启,并且系统能找到对应的Python库文件,不同系统的Python路径配置可能有差异
GoPythoncgogolang_python修改时间:2026-06-13 02:51:54