我在使用 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
{
我还没有找到如何提取异常的方法,或者......没有测试每个异常
}
}
我什至接近做到这一点了吗?就像我说的,它大部分都有效,但我真的很想了解为什么我会收到错误。
提前感谢您的帮助。
最佳答案
您可以随心所欲地调用 PyImport_Import()
,但您只会不断得到相同的模块对象。 Python 缓存导入。此外,与其创建新的 Python 字符串并泄漏引用(以及对象),不如使用 PyImport_ImportModule()
,它采用 const char *
。
PyImport_Import*()
返回一个新引用,不过,您应该在完成后对其调用 Py_DECREF()
。将模块存储在全局中应该不是问题,只要您拥有对它的引用(您在此处这样做。)
在调用 PyEval_CallObject()
时,您没有检查 Py_BuildValue()
的结果是否有错误,也没有调用 Py_DECREF( )
当你完成它时,你也会泄漏那个对象。
为了将 Python float 转换为 C double,您可能应该只调用 PyFloat_AsDouble()
而不是乱用 PyArg_Parse()
(记住测试异常)
深入到实际的错误处理:PyErr_ExceptionMatches()
仅在您实际想要测试异常是否匹配某些内容时才有用。如果您想知道是否发生异常,或获取实际的异常对象,您应该调用 PyErr_Occurred()
。它返回当前异常类型(不是实际的异常对象)作为借用的引用,如果没有设置则返回 NULL。如果您只想打印到 stderr 的回溯,PyErr_Print()
和 PyErr_Clear()
就是您要使用的。为了更细粒度地检查代码中的实际错误,PyErr_Fetch()
为您提供当前异常对象和与之关联的回溯(它为您提供与 sys.exc_info 相同的信息()
在 Python 代码中。)考虑到所有事情,您很少想深入了解 C 代码中的异常处理。
关于python - PyEval_CallObject 偶尔会在循环中失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2234539/