将Python嵌入C:无法从Python模块导入指定方法的问题
嘿,我来帮你搞定这个问题!你的代码里藏着几个关键问题,导致你遇到找不到方法和段错误的麻烦,咱们一步步捋清楚:
1. 错误处理完全用错了工具
你用perror来打印Python相关的错误是完全不对的!perror是用来处理系统调用错误的,它只认errno这个系统错误码,但Python的错误是靠自己的异常机制管理的,和errno八竿子打不着。你看到的Can't find method: Success就是因为此时系统根本没出错(errno是0),所以perror瞎输出了个Success,直接把真正的Python错误给掩盖了。
以后只要Python API函数返回NULL,就马上调用PyErr_Print(),这才能看到真正的错误原因,比如模块导入时的语法错误、方法名拼写错了之类的。
2. Python函数没返回值,但C代码硬要解析返回值
你的test.py里myfun()只做了计算和打印,根本没写return语句,所以它默认返回None。但你在C里用PyArg_Parse(ret, "&d", pValue)试图把None解析成double,这不仅会触发Python异常,而且pValue是个没初始化的野指针,直接就导致段错误了。
3. 还有俩小bug要改
printf里的格式字符串写错了,你写的"&d"应该是"%d"(如果是整数)或者"%f"(浮点数)pValue没初始化就用,这是典型的未定义行为,绝对不能这么干
先把test.py改了,给myfun加上返回值:
def myfun(): c = 123 + 123 print('the result is: ', c) return c # 必须加这个,不然返回None # 要是不想模块一导入就自动执行myfun,可以把下面这行注释掉 # myfun()
然后是修正后的main.c,我还加了对象引用计数的管理(Python API里必须手动管理对象内存):
#include <stdio.h> #include <python3.6/Python.h> int main() { PyObject *pModule, *pMethod, *ret; double result; // 用普通变量存结果,别用野指针 // 初始化Python解释器,先检查是否成功 if (!Py_Initialize()) { fprintf(stderr, "Python初始化失败\n"); return 1; } // 获取sys.path,把当前目录加进去,确保能找到test.py PyObject *sys = PyImport_ImportModule("sys"); if (!sys) { PyErr_Print(); Py_Finalize(); return 1; } PyObject *path = PyObject_GetAttrString(sys, "path"); if (!path) { PyErr_Print(); Py_DECREF(sys); Py_Finalize(); return 1; } if (PyList_Append(path, PyUnicode_FromString(".")) == -1) { PyErr_Print(); Py_DECREF(path); Py_DECREF(sys); Py_Finalize(); return 1; } // 用完的对象要记得释放,避免内存泄漏 Py_DECREF(path); Py_DECREF(sys); // 导入test模块 pModule = PyImport_ImportModule("test"); if (!pModule) { PyErr_Print(); Py_Finalize(); return 1; } // 获取myfun方法 pMethod = PyObject_GetAttrString(pModule, "myfun"); if (!pMethod) { PyErr_Print(); Py_DECREF(pModule); Py_Finalize(); return 1; } // 额外加个检查:确保拿到的是可调用对象(比如函数) if (!PyCallable_Check(pMethod)) { fprintf(stderr, "myfun不是可调用对象\n"); Py_DECREF(pMethod); Py_DECREF(pModule); Py_Finalize(); return 1; } // 调用方法,无参数 ret = PyEval_CallObject(pMethod, NULL); if (!ret) { PyErr_Print(); Py_DECREF(pMethod); Py_DECREF(pModule); Py_Finalize(); return 1; } // 解析返回值为double类型 if (!PyArg_Parse(ret, "d", &result)) { PyErr_Print(); Py_DECREF(ret); Py_DECREF(pMethod); Py_DECREF(pModule); Py_Finalize(); return 1; } // 打印结果 printf("myfun返回的结果是:%.0f\n", result); // 释放所有Python对象 Py_DECREF(ret); Py_DECREF(pMethod); Py_DECREF(pModule); // 关闭Python解释器 Py_Finalize(); return 0; }
编译的时候要确保链接正确的Python库,用下面的命令(根据你的Python版本调整):
gcc main.c -o main $(python3.6-config --cflags --ldflags)
然后把test.py和编译出来的main放在同一个目录下,运行./main就可以看到正确结果了。
myfun? 等你用了正确的错误处理(PyErr_Print()),就能看到真正的错误信息了。常见的原因包括:
test.py不在当前目录,或者sys.path没包含它的路径(不过你已经加了当前目录,这个可能性不大)test.py里有语法错误,导致模块看似导入成功但实际有异常- 方法名拼写错了(Python是大小写敏感的,比如
MyFun和myfun是俩东西)
内容的提问来源于stack exchange,提问作者Saeley

