PyEval_CallObject偶尔会在循环中失败

时间:2010-02-10 05:07:12

标签: python c python-c-api

我在使用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
       {

我还没有找到如何提取异常,或者......没有测试每个异常

       }
    }

我甚至接近这样做了吗?就像我说的,它主要起作用,但我真的很想理解为什么我会收到错误。

提前感谢您的帮助。

1 个答案:

答案 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代码中的异常处理。