gopy是最主流的将Go代码编译为原生Python扩展模块(.so/.pyd)的工具,基于CGO和Python C API,生成可直接import的二进制模块而非.py文件。

gopy 是目前最直接、最主流的将 Go 代码暴露为 Python 模块的方式。它不是“创建 .py 文件”,而是生成可被 import 的原生扩展模块(.so / .pyd),底层依赖 CGO 和 Python C API。
gopy build 生成的不是 .py,而是原生扩展模块
gopy build 命令不会产出 Python 源码(.py),它调用 go build -buildmode=c-shared 生成动态库,并配套生成一个轻量级的 Python 包壳(含 init.py 和 setup.py)。最终你 import 的是编译后的二进制模块,不是文本文件。
- 生成物示例(Linux/macOS):
-
mymodule.so(核心扩展) -
mymodule/init.py(自动封装,把.so加载进来)
-
- Windows 下是
mymodule.pyd,行为一致
常见错误现象:
运行 python -c "import mymodule" 报 ImportError: No module named 'mymodule' —— 很可能因为没在当前目录下执行,或没先运行 pip install -e .(如果用了 gopy pkg)。
使用场景:
适合已有 Go 函数想被 Python 调用,且不希望用户安装 Go 工具链;不适合需要纯 Python 运行时或跨平台免编译分发的场景。
Go 源码必须带 //export + C 兼容签名
Python 只能调用 C ABI 兼容的函数。gopy 会扫描 //export 注释并检查函数签名是否满足 C 互操作要求。
- 必须写法:
package main
import "C"
//export Add func Add(a, b int) int { return a + b }
func main() {}
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 容易踩的坑:
-
//export和import "C"之间不能有空行(CGO 规则) - 参数/返回值类型必须是 C 兼容基础类型:
int,float64,*C.char,C.int等;不能用string,slice,struct - 如果要用字符串,得手动转:
C.CString("hello")/C.GoString(cstr) -
main()函数不能省略(即使为空),否则gopy无法识别包入口
-
gopy pkg vs gopy build:选哪个取决于分发方式
gopy build -output=out -vm=python3 github.com/xxx/mymodule
→ 生成out/目录,含.so和最小化 Python 包结构,适合本地测试或 CI 构建后手动复制。gopy pkg github.com/xxx/mymodule
→ 生成完整可 pip 安装的包(含setup.py,pyproject.toml,MANIFEST.in),支持pip install -e .或pip install ./dist/*.whl。
适合要发布到 PyPI 或团队内共享的场景。
性能 / 兼容性影响:
两者生成的扩展模块完全一致,区别只在打包方式。pkg 会自动处理 Python 版本兼容标签(如 cp39-cp39-manylinux2014_x86_64),而 build 不管这些,需自行控制构建环境。
Windows 上 python3.exe 找不到?检查 PATH 和符号链接
gopy 默认调用 python3 命令来生成绑定元信息。Windows 用户常遇到:
exec: "python3": executable file not found in $PATH- 或报错
ModuleNotFoundError: No module named 'pybindgen'(因gopy依赖 Python 侧工具)
解决方法:
- 确保已安装 Python(不要用 Microsoft Store 版本,它不提供
python3.exe) - 手动创建符号链接:在 Python 安装目录下运行
mklink python3.exe python.exe
- 或用
-vm指定绝对路径:gopy build -vm="C:\Python39\python.exe" ./mymodule
另外,pybindgen 必须用 Python 3.7+ 安装,且建议用 pip install pybindgen 而非 conda,后者有时版本不匹配。
真正麻烦的不是命令怎么敲,而是 Go 函数签名和 Python 类型之间的映射边界 —— 字符串、切片、错误处理、内存生命周期,这些都得靠显式转换。一旦涉及复杂数据结构,不如直接走 HTTP 或子进程通信更可控。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










