3

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

我还没能找到如何提取异常,要么......没有测试每个异常

       }
    }

我什至接近做到这一点吗?就像我说的,它主要是有效的,但我真的很想了解为什么我会出错。

预先感谢您的任何帮助。

4

1 回答 1

5

您可以随时调用PyImport_Import(),但您只会不断地获取相同的模块对象。Python 缓存导入。PyImport_ImportModule()此外,不要创建新的 Python 字符串并泄漏引用(以及对象),而应该只使用const char *.

PyImport_Import*()返回一个新的引用,但是,你应该Py_DECREF()在完成后调用它。将模块存储在全局中应该不是问题,只要您拥有对它的引用(您在此处执行此操作。)

在你打电话给PyEval_CallObject()你时,你并没有检查Py_BuildValue()错误的结果,当你完成它时你也没有打电话Py_DECREF(),所以你也泄漏了那个对象。

为了将 Python 浮点数转换为 C 双精度数,您可能应该只调用PyFloat_AsDouble()而不是乱用PyArg_Parse()(并记住要测试异常)

到实际的错误处理:PyErr_ExceptionMatches()仅当您真正想要测试异常是否匹配某些东西时才有用。如果您想知道是否发生了异常,或者获取实际的异常对象,PyErr_Occurred()那么您应该调用它。它返回当前异常类型(不是实际异常对象)作为借用引用,如果没有设置,则返回 NULL。如果您只想打印到 stderr 的回溯,PyErr_Print()并且PyErr_Clear()是您想要使用的。为了更细粒度地检查代码中的实际错误,为您PyErr_Fetch()获取当前异常对象和与之关联的回溯(它为您提供与sys.exc_info()Python 代码中相同的信息。)所有被认为是您很少想要深入了解的事情C代码中的异常处理。

于 2010-02-10T05:19:45.423 回答