如何创建和使用存储为 c++ object 的 python object?

How to create and use a python object stored as a c++ object?

所以正如标题所暗示的那样,我正在开发一个 c++ 项目,我需要调用一个 python 模块,将其另存为 object,并多次调用其中一个方法次。您可以在下面找到 class 的代码,其中包含 python object。目前,它是在多次调用该方法的 for 循环中实现的。实例化 class 以及对 class 的第一次调用工作正常。但是,在 for 循环的第一个循环完成后,程序崩溃并出现 "free(): invalid size" 或有时 "double free or corruption" 行的错误。我尝试使用 valgrind 来尝试追踪内存泄漏,但我得到了很多我不太了解的 pythonCApi 调用的痕迹。

#include <python2.7/Python.h>
#include <iostream>
#include <algorithm>
#include "predictor.hpp"

using namespace std;


predictor::predictor()
{
  Py_Initialize();

  pName = PyString_FromString("predictor");

  pModule = PyImport_Import(pName);

  Py_XDECREF(pName);

  if (pModule == nullptr) {
    PyErr_Print();
    std::cerr << "Fails to import the module predictor, check installation.\n";
  }

  // dict is a borrowed reference.
  dict = PyModule_GetDict(pModule);
  if (dict == nullptr) {
    PyErr_Print();
    std::cerr << "Fails to get the dictionary, check predictor installation.\n";
    Py_XDECREF(pModule);
  }
  Py_XDECREF(pModule);

  // Builds the name of a callable class
  python_class = PyDict_GetItemString(dict, "Predictor");
  if (python_class == nullptr || python_class == NULL) {
    PyErr_Print();
    std::cerr << "Fails to get the Python class, check predictor installation.\n";
    Py_XDECREF(dict);
  }
  Py_XDECREF(dict);

  // Creates an instance of the class
  if (PyCallable_Check(python_class)) {
    object = PyObject_CallObject(python_class, nullptr);
    if (object == NULL)
    {
        cerr << "Fails to create object.";
        Py_XDECREF(python_class);
    }
    Py_XDECREF(python_class);
} else {
    PyErr_Print();
    std::cout << "Cannot instantiate the Python class" << std::endl;
    Py_XDECREF(python_class);
}

pMethod = PyString_FromString("predict_all");

}


predictor::~predictor()
{

  Py_XDECREF(pMethod);

  Py_XDECREF(object);
  Py_Finalize();

}


long predictor::predict(string rule)
{

  PyObject *pRule = PyString_FromString(rule.c_str());
  PyObject *value = PyObject_CallMethodObjArgs(object, pMethod, pRule, NULL);

  long endValue = PyInt_AsLong(value);

  if (endValue == -1)
  {
    if(!PyErr_Occurred())
    {

        PyErr_Print();
        cerr << "";

        Py_XDECREF(value);
        Py_XDECREF(pRule); 

        return NULL;
    }

    //PyErr_Print();

  }

  Py_XDECREF(value);
  Py_XDECREF(pRule); 

  return endValue;}

编写 Python C/C++ 代码最关键的部分是获得正确的引用计数。 Python 区分不同类型的引用,即 newstolenborrowed 引用。

对于您调用的每个 API 函数,您必须检查文档以查看它 return 的参考类型,如果有的话。

新引用属于调用者,因此使用 Py_XDECREF 通过减少引用计数来释放对象是正确的。请确保不要多次调用 Py_XDECREF ,除非您在这期间增加了引用计数。在您的错误处理中,Py_XDECREF(pModule) 发生了两次,例如,因为在错误情况下您没有 return,您只需继续。

借用的参考资料归其他人所有,您的参考计数没有增加。因此,调用 Py_XDECREF 只有在您自己增加引用计数后才有效。

PyModule_GetDict(pModule) return 是借用的参考。您不增加引用计数,但稍后使用 Py_XDECREF(dict) 减少它。 PyDict_GetItemString(dict, "predictor") 也是如此,它 return 是一个借用的引用,但你用 Py_XDECREF(python_class) 减少它。

我的假设是,在这两种情况下(dictpython_class),借用的引用都属于您使用 PyImport_Import(pName) 导入的模块 pModule。因此,只要您正在使用 pModule 拥有的借用引用,您就很有可能不能减少 pModule 引用计数。一旦您不再使用那些借用的引用,请将 pModulePy_XDECREF 一起发布。或者,您可以增加借用引用的引用计数,但只要您保留 pModule,就没有必要这样做。