我在使用Python C API时遇到了一些麻烦。我正在调用python方法在大约60hz做一些游戏AI。它的工作时间大多数,但每隔一秒左右调用PyEval_CallObject就会产生一个NULL返回值。如果我正确检测到错误并继续循环,那么下一秒左右一切都很好,于是再次发生错误。
我怀疑我在引用计数方面做错了但我无法弄清楚它是什么:
int script_do_ai(struct game_data_t* gd)
{
PyObject *pAiModule, *pResult;
float result=0.0;
pResult = NULL;
pAiModule = PyImport_Import(PyString_FromString("ai_script"));
是的,我每次迭代都会导入模块。这有必要吗?如果我将pAiModule存储为全局,我会在大约一秒后发生严重崩溃。
pResult = PyEval_CallObject(PyObject_GetAttrString(pAiModule, "do_ai"),
Py_BuildValue("f", gd->important_float))
if (pResult != NULL)
{
PyArg_Parse(pResult, "f", &result);
Py_DECREF(pResult);
ConquerEnemies(result); //you get the idea
}
else //this happens every 75 or so iterations thru the loop
{
if (PyErr_ExceptionMatches(PyExc_SomeException)) //? not sure what to do here
{
我还没有找到如何提取异常,或者......没有测试每个异常
}
}
我甚至接近这样做了吗?就像我说的,它主要起作用,但我真的很想理解为什么我会收到错误。
提前感谢您的帮助。
答案 0 :(得分:4)
您可以根据需要随时调用PyImport_Import()
,但您只需继续获取相同的模块对象。 Python缓存导入。此外,您应该使用PyImport_ImportModule()
,而不是创建新的Python字符串并泄漏引用(以及对象),而只需const char *
。
PyImport_Import*()
会返回一个新的引用,但是当你完成时,你应该在它上面调用Py_DECREF()
。将模块存储在全局中应该不是问题,只要你拥有对它的引用(你在这里做)。
在致电PyEval_CallObject()
时,您没有检查Py_BuildValue()
的错误结果,而且在您完成错误时也没有致电Py_DECREF()
,所以你也在泄漏这个对象。
为了将Python float转换为C double,您可能只需调用PyFloat_AsDouble()
而不是与PyArg_Parse()
混淆(并记住测试异常)
直到实际的错误处理:PyErr_ExceptionMatches()
仅在您真正想要测试异常是否匹配时才有用。如果您想知道是否发生了异常,或者获取了实际的异常对象,那么您应该调用PyErr_Occurred()
。它将当前异常 type (不是实际异常对象)作为借用引用返回,如果没有设置则返回NULL。如果您只想将追溯打印到stderr,PyErr_Print()
和PyErr_Clear()
就是您想要使用的。为了更细致地检查代码中的实际错误,PyErr_Fetch()
将获取当前异常对象以及与之关联的回溯(它在Python代码中为您提供与sys.exc_info()
相同的信息。)全部认为你很少想深入了解C代码中的异常处理。