PyEval_CallObject在循环中偶尔失败
我在使用Python的C接口时遇到了一些问题。我每秒大约调用60次一个Python方法来处理游戏的人工智能。大部分时间这个调用都能正常工作,但每隔一秒左右,调用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
{
我还没有找到如何提取异常的方法……而且不想测试每一个异常。
}
}
我这样做是不是差不多正确?就像我说的,它大部分时间都能工作,但我真的想理解为什么会出现错误。
提前感谢任何帮助。
1 个回答
你可以随便调用 PyImport_Import()
,但每次得到的都是同一个模块对象。Python 会缓存导入的模块。还有,不用创建新的 Python 字符串并泄露引用(也就是对象),你可以直接使用 PyImport_ImportModule()
,这个函数需要一个 const char *
类型的参数。
PyImport_Import*()
会返回一个新的引用,不过在用完之后你应该调用 Py_DECREF()
来减少引用计数。把模块存储在全局变量里应该没问题,只要你持有对它的引用(在这里你是有的)。
在你调用 PyEval_CallObject()
的时候,你没有检查 Py_BuildValue()
的结果是否有错误,而且在用完之后也没有调用 Py_DECREF()
,这样会导致对象泄露。
如果你想把 Python 的浮点数转换成 C 的双精度浮点数,最好直接调用 PyFloat_AsDouble()
,而不是去搞 PyArg_Parse()
(记得要检查异常哦)。
说到错误处理:PyErr_ExceptionMatches()
只有在你想测试异常是否匹配某个特定情况时才有用。如果你想知道是否发生了异常,或者获取实际的异常对象,应该调用 PyErr_Occurred()
。这个函数会返回当前异常的类型(不是实际的异常对象),如果没有异常则返回 NULL。如果你只是想打印错误追踪信息到标准错误输出,可以使用 PyErr_Print()
和 PyErr_Clear()
。如果你想更详细地检查代码中的实际错误,PyErr_Fetch()
可以获取当前的异常对象和相关的追踪信息(它提供的信息和 Python 代码中的 sys.exc_info()
是一样的)。总的来说,你在 C 代码中很少需要深入到异常处理的细节里。