使用pybind11封装Go代码制Python库时遇ImportError求助
问题:通过pybind11调用Go代码生成Python库时遇ImportError
场景与操作步骤
- 在Anaconda虚拟环境
goscraper中安装pybind11,使用VSCode开发 - 创建
add.go文件,编写导出的Go函数 - 执行
go build -o addlib.dll -buildmode=c-shared add.go生成addlib.dll和addlib.h - 创建
addlib.cpp,编写pybind11绑定代码 - 配置
c_cpp_properties.json添加头文件路径 - 编写
CMakeLists.txt,通过VS2022 MSVC开发者命令提示符在build目录执行cmake ..和cmake --build .编译
错误信息
导入生成的Python库时出现:
ImportError: dynamic module does not define module export function
相关代码
add.go
package main import "C" import "fmt" //export Add func Add(a int, b int) int { return a + b } //export Greet func Greet() { fmt.Println("Hello world") } func main() { }
addlib.cpp
#include <pybind11/pybind11.h> extern "C" { #include "addlib.h" } namespace py = pybind11; int add(int a, int b) { return Add(a, b); } void greet() { Greet(); } PYBIND11_MODULE(Go_Pybind_practice_v2, m) { m.def("add", &add, "Add two integers"); m.def("greet", &greet, "Print greeting from Go"); }
c_cpp_properties.json
{ "configurations": [ { "name": "Win32", "includePath": [ "${workspaceFolder}/**", "C:\\dev\\go_pybind_practice_v3\\pybind11\\include\\pybind11", // for pybind11.h "C:\\Users\\accra\\.conda\\envs\\goscraper\\include", // for Python.h "C:\\Users\\accra\\.conda\\envs\\goscraper\\Library\\share\\cmake\\pybind11" // for pybind11Config.cmake ], "defines": [ "_DEBUG", "UNICODE", "_UNICODE" ], "windowsSdkVersion": "10.0.22621.0", "compilerPath": "cl.exe", "cStandard": "c17", "cppStandard": "c++17", "intelliSenseMode": "windows-msvc-x64" } ], "version": 4 }
CMakeLists.txt
cmake_minimum_required(VERSION 3.15) project(go_pybind_practice_v3) set(pybind11_DIR "C:\\Users\\accra\\.conda\\envs\\goscraper\\Library\\share\\cmake\\pybind11") find_package(pybind11 REQUIRED) pybind11_add_module(go_pybind_practice_v3 addlib.cpp) target_link_libraries(go_pybind_practice_v3 PRIVATE pybind11::module "C:\\dev\\go_pybind_practice_v3\\addlib.lib")
addlib.h
/* Code generated by cmd/cgo; DO NOT EDIT. */ /* package command-line-arguments */ #line 1 "cgo-builtin-export-prolog" #include <stddef.h> #ifndef GO_CGO_EXPORT_PROLOGUE_H #define GO_CGO_EXPORT_PROLOGUE_H #ifndef GO_CGO_GOSTRING_TYPEDEF typedef struct { const char *p; ptrdiff_t n; } _GoString_; #endif #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_t GoUintptr; typedef float GoFloat32; typedef double GoFloat64; #ifdef _MSC_VER #include <complex.h> typedef _Fcomplex GoComplex64; typedef _Dcomplex GoComplex128; #else typedef float _Complex GoComplex64; typedef double _Complex GoComplex128; #endif /* 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]; #ifndef GO_CGO_GOSTRING_TYPEDEF typedef _GoString_ GoString; #endif 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 __declspec(dllexport) GoInt Add(GoInt a, GoInt b); extern __declspec(dllexport) void Greet(); #ifdef __cplusplus } #endif
解决方案
1. 修正模块名称不匹配问题
这是触发错误的核心原因:
CMakeLists.txt中pybind11_add_module指定的模块名是go_pybind_practice_v3addlib.cpp中PYBIND11_MODULE的第一个参数是Go_Pybind_practice_v2
两者必须完全一致,Python导入时会寻找与模块名对应的导出函数。修改其中一处使名称统一,比如将PYBIND11_MODULE的名称改为go_pybind_practice_v3:
PYBIND11_MODULE(go_pybind_practice_v3, m) { m.def("add", &add, "Add two integers"); m.def("greet", &greet, "Print greeting from Go"); }
2. 修正类型匹配问题
Go生成的Add函数参数和返回值是GoInt(对应long long),但当前cpp中的add函数使用int类型,可能导致类型截断。修改cpp中的类型匹配:
#include <pybind11/pybind11.h> extern "C" { #include "addlib.h" } namespace py = pybind11; GoInt add(GoInt a, GoInt b) { return Add(a, b); } void greet() { Greet(); } PYBIND11_MODULE(go_pybind_practice_v3, m) { m.def("add", &add, "Add two integers"); m.def("greet", &greet, "Print greeting from Go"); }
3. 确保Go生成的DLL可被加载
编译完成后,将addlib.dll复制到生成的.pyd文件所在目录,或者将addlib.dll的路径添加到系统PATH环境变量中,否则Python导入模块时会找不到依赖的Go动态库。
内容的提问来源于stack exchange,提问作者Mark Kim
相关产品推荐
相关产品推荐

