Python C扩展导入报错:模块未定义PyInit_library导出函数
你的核心问题出在模块初始化函数的命名不匹配,这是Python C扩展必须遵守的硬性规则:初始化函数的名字必须严格是PyInit_<你的模块名>,其中<你的模块名>要和setup.py里Extension的第一个参数完全一致。
第一步:修复初始化函数名(解决导入报错)
看你的代码:
setup.py里定义的模块名是'library':module = Extension('library', sources = ['library.c'])- 但你的C代码里的初始化函数是
PyInit_myModule:PyMODINIT_FUNC PyInit_myModule(void) { Py_Initialize(); return PyModule_Create(&myModule); }
这就导致Python加载模块时找不到PyInit_library函数,直接触发报错。把函数名改成PyInit_library,同时注意去掉Py_Initialize()调用——模块加载时Python已经完成初始化了,手动调用会引发问题:
PyMODINIT_FUNC PyInit_library(void) { return PyModule_Create(&myModule); }
第二步:修复其他潜在问题(否则导入后运行会出错)
解决了导入问题后,你的代码还有几个关键bug需要修复:
1. 结构体定义语法错误
你的Library.h里的typedef struct后面多了多余的分号,导致无法直接用Book或Library作为类型名:
// 原错误代码 typedef struct Book { char *book_name; char *book_type; char *book_author; int numOfPages; }; // 这里的分号多余,应该改成} Book; typedef struct Library { struct Book Books[100]; int booksStored; }; // 同理,改成} Library;
修正后:
typedef struct Book { char *book_name; char *book_type; char *book_author; int numOfPages; } Book; typedef struct Library { Book Books[100]; int booksStored; } Library;
2. 字符串比较逻辑错误
C语言中不能用==比较字符串内容(这只会比较指针地址),必须用strcmp函数(记得包含<string.h>头文件)。同时你还写错了数组元素的访问方式:
// 原错误代码 if (lib->Books->book_name == bkname) {
修正为:
#include <string.h> // 必须添加这个头文件 // ... if (strcmp(lib->Books[i].book_name, bkname) == 0) {
3. Python参数解析错误
你用了"O&s"格式符,但O&需要配套转换函数才能把Python对象转换成C结构体指针——Python里没有直接对应C结构体的原生对象。这里推荐两种可行的解决思路:
- 思路一:在C扩展内封装Library结构体
添加一个创建Library的函数,用PyCapsule封装指针返回给Python,后续函数从胶囊中取出指针:
然后在static PyObject* create_library(PyObject* self) { Library* lib = malloc(sizeof(Library)); lib->booksStored = 0; return PyCapsule_New(lib, "Library", free); // 自动释放内存 }myMethods里添加这个函数,解析参数时用O格式符取出胶囊:static PyObject* addBook(PyObject* self, PyObject* args) { PyObject* capsule; char *bookname; char * booktype; char *bookauthor; int numOfPages; if (!PyArg_ParseTuple(args, "Osssi", &capsule, &bookname, &booktype, &bookauthor, &numOfPages)) return NULL; Library* lib = PyCapsule_GetPointer(capsule, "Library"); if (!lib) return NULL; // 返回Python布尔值,而不是用错误的"p"格式符 return PyBool_FromLong(CaddBook(lib, bookname, booktype, bookauthor, numOfPages)); } - 思路二:用Python的ctypes定义对应结构体
在Python代码中用ctypes定义和C一致的结构体,直接传递给扩展函数,这种方式需要调整参数解析逻辑。
4. 返回值构建错误
你用Py_BuildValue("p", CaddBook(...))返回bool值,但"p"是用来处理char*的,会把bool值转换成指针地址,导致Python得到错误结果。应该用PyBool_FromLong返回原生Python布尔值:
return PyBool_FromLong(CaddBook(lib, bookname, booktype, bookauthor, numOfPages));
5. 其他小优化
version函数不需要强制转换(PyCFunction),直接写version即可:{"version", version, METH_NOARGS, "Returns the version."},
最后重新编译
修改完所有代码后,重新执行编译命令:
sudo python3 setup.py build_ext --inplace
现在应该可以正常import library并调用函数了。
内容的提问来源于stack exchange,提问作者Damian Thompson

