Python调用go function

本文介绍了一个Python代码调用Go语言编写的函数的实际案例。通过ctypes库和cgo,实现了Python与Go之间的互操作,展示了如何定义Go字符串结构体、加载Go库、传递参数并接收返回值。

Python code foo.py

from ctypes import *
from ctypes import cdll
import time

class go_string(Structure):
    _fields_ = [
        ("p", c_char_p),
        ("n", c_int)]

lib = cdll.LoadLibrary('/var/tmp/foo.so')

def bar(str):
    b = go_string(c_char_p(bytes(str, "utf-8")), len(str))
    lib.bar.restype = c_char_p
    a = lib.bar(b, c_char_p(bytes(str, "utf-8")))
    print(a)

bar("haha")
bar("hoho")
time.sleep(100)

Go code foo.go

package main

import (
    "C"
    "fmt"
    "time"
)

//export bar
func bar(a string, b *C.char) *C.char {
    c := a
    d := C.GoString(b)
    go func() {
        for {
            time.Sleep(time.Second)
            fmt.Println("a:", a)
            fmt.Println("b:", C.GoString(b))
            fmt.Println("c:", c)
            fmt.Println("d:", d)
        }
    }()
    return C.CString("hello from go")
}

func main() {}

编译go

go build -buildmode=c-shared -o foo.so foo.go

编译生成的foo.h

/* Code generated by cmd/cgo; DO NOT EDIT. */

/* package command-line-arguments */


#line 1 "cgo-builtin-prolog"

#include <stddef.h> /* for ptrdiff_t below */

#ifndef GO_CGO_EXPORT_PROLOGUE_H
#define GO_CGO_EXPORT_PROLOGUE_H

typedef struct { const char *p; ptrdiff_t n; } _GoString_;

#endif

/* Start of preamble from import "C" comments.  */




/* End of preamble from import "C" comments.  */


/* Start of boilerplate cgo prologue.  */
#line 1 "cgo-gcc-export-header-prolog"

#ifndef GO_CGO_PROLOGUE_H
#define GO_CGO_PROLOGUE_H

typedef signed char GoInt8;
typedef unsigned char GoUint8;
typedef short GoInt16;
typedef unsigned short GoUint16;
typedef int GoInt32;
typedef unsigned int GoUint32;
typedef long long GoInt64;
typedef unsigned long long GoUint64;
typedef GoInt64 GoInt;
typedef GoUint64 GoUint;
typedef __SIZE_TYPE__ GoUintptr;
typedef float GoFloat32;
typedef double GoFloat64;
typedef float _Complex GoComplex64;
typedef double _Complex GoComplex128;

/*
  static assertion to make sure the file is being used on architecture
  at least with matching size of GoInt.
*/
typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];

typedef _GoString_ GoString;
typedef void *GoMap;
typedef void *GoChan;
typedef struct { void *t; void *v; } GoInterface;
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;

#endif

/* End of boilerplate cgo prologue.  */

#ifdef __cplusplus
extern "C" {
#endif


extern char* bar(GoString p0, char* p1);

#ifdef __cplusplus
}
#endif

运行

在这里插入图片描述

Reference

  • https://gist.github.com/helinwang/4f287a52efa4ab75424c01c65d77d939
<think>我们被要求使用Go语言调用现有的Python模块。根据提供的引用,有几种方法可以实现: 1. 将Go代码编译成动态库,然后通过Python的ctypes调用(这是Python调用Go,但我们需要的是Go调用Python) 2. 使用CGOPython的C API,通过Go调用Python(这正是我们需要的) 3. 使用第三方库,如go-python(针对Python2)或go-python3(针对Python3) 根据引用[2][3][4],我们可以使用go-python(针对Python2)或go-python3(针对Python3)来调用Python模块。由于现在Python3是主流,我们选择go-python3。 步骤: 1. 安装go-python3库 2. 初始化Python解释器 3. 导入Python模块并调用函数 注意:使用go-python3需要系统中已安装Python3的开发包(例如,在Ubuntu上需要安装python3-dev)。 示例代码: 假设我们有一个Python模块`hello.py`,内容如下: ```python # hello.py def greet(name): return f"Hello, {name}!" ``` 我们想在Go调用这个`greet`函数。 首先,安装go-python3: ```bash go get github.com/DataDog/go-python3 ``` 然后,在Go代码中: ```go package main import ( "fmt" "github.com/DataDog/go-python3" ) func main() { // 初始化Python解释器 python3.Py_Initialize() // 确保在程序结束时关闭解释器 defer python3.Py_Finalize() // 将当前目录添加到Python的模块搜索路径(可选,如果模块不在当前目录则需设置路径) sysModule := python3.PyImport_ImportModule("sys") path := sysModule.GetAttrString("path") python3.PyList_Insert(path, 0, python3.PyUnicode_FromString(".")) // 添加当前目录 // 导入hello模块 helloModule := python3.PyImport_ImportModule("hello") if helloModule == nil { panic("Error importing module") } // 获取greet函数 greetFunc := helloModule.GetAttrString("greet") if greetFunc == nil { panic("Error getting function greet") } // 调用函数,传递一个字符串参数 args := python3.PyTuple_New(1) python3.PyTuple_SetItem(args, 0, python3.PyUnicode_FromString("World")) result := greetFunc.Call(args, python3.PyDict_New()) if result == nil { panic("Error calling function") } // 处理结果 fmt.Println(python3.PyUnicode_AsUTF8(result)) } ``` 注意:上面的代码使用了go-python3,它是对Python C API的封装。需要确保编译时链接了Python3的库。 编译时需要指定CGO标志,例如: ```bash export CGO_CFLAGS="-I/usr/include/python3.8" export CGO_LDFLAGS="-L/usr/lib/x86_64-linux-gnu -lpython3.8" go build -o main main.go ``` 请根据系统中Python3的实际安装路径和版本调整上述标志。 另一种方法是使用exec.Command调用Python脚本并获取输出,但这种方法不适合需要频繁调用或传递复杂数据的情况。 总结: 使用go-python3库可以在Go中直接调用Python模块,但需要配置正确的CGO环境。 相关问题: 1. 如何传递复杂的数据结构(如列表、字典)? 2. 如何处理Python中的异常? 3. 有没有其他更简单的方法在Go调用Python? 4. 如何管理Python解释器的生命周期?</think>### 在 Go调用 Python 模块的完整指南 根据引用内容,Go 语言调用 Python 模块主要有以下三种方法,按实现复杂度排序: --- #### 方法 1:使用 `go-python`/`go-python3` 库(推荐) **原理**:通过 Python C API 实现 GoPython 的互操作[^2][^3] ```go package main import ( "fmt" python3 "github.com/DataDog/go-python3" ) func main() { // 初始化 Python 解释器 python3.Py_Initialize() defer python3.Py_Finalize() // 确保释放资源 // 导入 Python 模块 module := python3.PyImport_ImportModule("your_module") if module == nil { panic("导入模块失败") } // 调用函数(假设函数名为 add) funcName := python3.PyUnicode_FromString("add") defer funcName.DecRef() args := python3.PyTuple_New(2) python3.PyTuple_SetItem(args, 0, python3.PyLong_FromLong(3)) python3.PyTuple_SetItem(args, 1, python3.PyLong_FromLong(5)) result := module.CallMethodObjArgs(funcName, args) fmt.Printf("结果: %v\n", python3.PyLong_AsLong(result)) } ``` **安装**: ```bash # Python3 支持 go get github.com/DataDog/go-python3 # Python2 支持 go get github.com/sbinet/go-python ``` **特点**: - 直接内存交互,无进程启动开销 - 支持复杂数据类型传递 - 需系统安装 Python 开发包(如 `python3-dev`) - 适合高频调用场景[^3] --- #### 方法 2:通过命令行调用 **原理**:使用 `exec.Command` 执行 Python 脚本并捕获输出 ```go package main import ( "fmt" "os/exec" ) func main() { // 执行 Python 脚本 cmd := exec.Command("python3", "-c", `import your_module; print(your_module.add(3,5))`) // 捕获输出 output, err := cmd.CombinedOutput() if err != nil { panic(fmt.Sprintf("执行失败: %v", err)) } fmt.Printf("结果: %s", output) // 输出: 8 } ``` **特点**: - 无需额外依赖 - 实现简单 - 每次调用需启动新进程 - 适合低频简单调用[^4] --- #### 方法 3:编译为 C 扩展模块 **原理**:将 Go 代码编译为 `.so` 动态库,通过 Python 的 `ctypes` 调用(反向调用) ```go // main.go package main import "C" //export Add func Add(a, b C.int) C.int { return a + b } func main() {} // 必需的入口函数 ``` 编译为动态库: ```bash go build -buildmode=c-shared -o libadd.so main.go ``` Python 调用: ```python # test.py from ctypes import CDLL lib = CDLL("./libadd.so") print(lib.Add(3, 5)) # 输出 8 ``` **特点**: - 高性能(编译为原生代码) - 适合计算密集型任务 - 实现复杂度较高 - 需处理 C 数据类型转换[^1] --- ### 关键选择建议 | 方法 | 适用场景 | 性能 | 实现复杂度 | |---------------------|-----------------------------------|--------|------------| | go-python3 | 高频交互/复杂数据传递 | ★★★ | ★★ | | 命令行调用 | 简单脚本/低频调用 | ★ | ★ | | C 扩展模块 | 计算密集型任务/已有 Python 项目 | ★★★ | ★★★ | --- ### 常见问题解决方案 1. **数据类型转换** - 使用 `PyLong_FromLong()` 转换整数 - 用 `PyUnicode_FromString()` 处理字符串 - 复杂结构需通过 JSON 序列化传递 2. **错误处理** ```go if python3.PyErr_Occurred() != nil { python3.PyErr_Print() panic("Python 运行时错误") } ``` 3. **环境配置** Linux 安装开发包: ```bash sudo apt-get install python3-dev # Ubuntu/Debian ``` --- ### 相关问题 1. 如何在 GoPython 间传递 NumPy 数组? 2. 调用 Python 时如何避免内存泄漏? 3. 使用 `go-python3` 时出现 `undefined reference` 错误如何解决? 4. 有没有更轻量级的 Go-Python 互操作方案? 5. 如何异步调用 Python 函数并获取结果? [^1]: 通过编译为动态库实现 Python 调用 Go 的扩展模块 [^2]: 使用 go-python 库通过 Python C API 实现互操作 [^3]: go-python3 库实现 Python3 调用 [^4]: 通过 Python/C API 在 Go调用 Python 模块
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值