Python PyEval_CallObject偶尔在循环中失败

Python PyEval_CallObject偶尔在循环中失败,python,c,python-c-api,Python,C,Python C Api,我在Python C API方面有点困难。我正在调用python方法来以大约60hz的频率执行一些游戏AI。它在大多数情况下都能工作,但对PyEval_CallObject的调用每隔一秒左右就会产生一个空返回值。如果我正确地检测到错误并继续循环,那么在下一秒左右的时间内一切都会正常,然后错误再次发生 我怀疑我的ref计数有问题,但我不知道这是什么: int script_do_ai(struct game_data_t* gd) { PyObject *pAiModule, *pRes

我在Python C API方面有点困难。我正在调用python方法来以大约60hz的频率执行一些游戏AI。它在大多数情况下都能工作,但对PyEval_CallObject的调用每隔一秒左右就会产生一个空返回值。如果我正确地检测到错误并继续循环,那么在下一秒左右的时间内一切都会正常,然后错误再次发生

我怀疑我的ref计数有问题,但我不知道这是什么:

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\u Import()
,但您只需不断获取相同的模块对象即可。Python缓存导入。另外,您不应该创建新的Python字符串并泄漏引用(以及对象),而应该只使用
PyImport\u ImportModule()
,它采用
const char*

PyImport\u Import*()
返回一个新引用,但是,完成后应该调用它的
Py\u DECREF()
。只要您拥有一个对模块的引用(在这里您是这样做的),将模块存储在全局文件中应该不会有问题

在对
PyEval\u CallObject()
的调用中,您没有检查
Py\u BuildValue()
的结果是否有错误,并且在处理完后也没有调用
Py\u DECREF()
,因此也泄漏了该对象

为了将Python float转换为C double,您可能只需要调用
PyFloat\u AsDouble()
,而不是使用
PyArg\u Parse()
(请记住测试异常)


具体到实际的错误处理:
PyErr\u ExceptionMatches()
仅在您真正想要测试异常是否与某些内容匹配时才有用。如果您想知道是否发生了异常,或者获取实际的异常对象,则应调用
PyErr\u occurrent()
。它将当前异常类型(不是实际的异常对象)作为借用引用返回,如果未设置,则返回NULL。如果您只想打印到stderr的回溯,
PyErr\u print()
PyErr\u Clear()
是您想要使用的。为了更细粒度地检查代码中的实际错误,
PyErr\u Fetch()
获取当前异常对象及其相关联的回溯(它获取的信息与Python代码中的
sys.exc\u info()
相同)考虑到所有的事情,您很少希望深入到C代码中的异常处理中。

+1谢谢……我想如果我内联调用Py_BuildValue,它会处理泄漏的引用,但可能不会。我来看看PyErr_Fetch在PyErr_OccurredAlas上给了我什么,C不是垃圾收集的:)PyErr_Print()对于打印异常非常方便,特别是在调试这样的案例时。是的,在.NET领域我花了太多时间。好吧,奇怪。我在python脚本中添加了“sys.stdout=open('CONOUT$,'wt')”(输出到控制台…我在Win32中),一切都开始神奇地工作。奇怪的是,我修好了程序错误。原来我运行的循环没有同步到60hz…它以100%的速度运行cpu。我投入了10毫秒的非繁忙等待,一切似乎都很好。它至少解释了为什么添加控制台输出“修复”了以前的问题……这是一个时间问题。如果没有上面的提示,就无法解决这个问题…谢谢!