rmp*_*251 1 python recursion segmentation-fault c-api
我在C++中使用python的C API(2.7)将python树结构转换为C++树.代码如下:
python树以递归方式实现为具有子列表的类.叶节点只是原始整数(不是类实例)
我加载模块并从C++调用蟒方法,使用从代码这里,它返回树,python_tree的一个实例,作为一个++中的PyObject℃.
递归遍历获得的PyObject.要获得孩子的名单,我这样做:
PyObject* attr = PyString_FromString("children");
PyObject* list = PyObject_GetAttr(python_tree,attr);
for (int i=0; i<PyList_Size(list); i++) {
PyObject* child = PyList_GetItem(list,i);
...
Run Code Online (Sandbox Code Playgroud)非常简单,它可以工作,直到我最终遇到分段错误,调用PyObject_GetAttr(Objects/object.c:1193,但我看不到API代码).它似乎发生在访问树的最后一个叶节点上.
我很难确定问题所在.使用C API进行递归是否有任何特殊注意事项?我不确定我是否需要使用Py_INCREF/Py_DECREF,或者使用这些函数.我并不完全理解API的工作原理.任何帮助深表感谢!
编辑:一些最小的代码:
void VisitTree(PyObject* py_tree) throw (Python_exception)
{
PyObject* attr = PyString_FromString("children");
if (PyObject_HasAttr(py_tree, attr)) // segfault on last visit
{
PyObject* list = PyObject_GetAttr(py_tree,attr);
if (list)
{
int size = PyList_Size(list);
for (int i=0; i<size; i++)
{
PyObject* py_child = PyList_GetItem(list,i);
PyObject *cls = PyString_FromString("ExpressionTree");
// check if child is class instance or number (terminal)
if (PyInt_Check(py_child) || PyLong_Check(py_child) || PyString_Check(py_child))
;// terminal - do nothing for now
else if (PyObject_IsInstance(py_child, cls))
VisitTree(py_child);
else
throw Python_exception("unrecognized object from python");
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
可以识别Python/C代码的几个问题:
PyObject_IsInstance 将类而不是字符串作为其第二个参数.
没有专用于引用计数的代码.新的引用(例如返回的PyObject_GetAttr引用)从未发布,并且在使用之前PyList_GetItem从未获取借用的引用.将C++异常与纯Python/C混合会加剧问题,使得实现正确的引用计数变得更加困难.
缺少重要的错误检查.PyString_FromString内存不足时可能会失败; PyList_GetItem如果列表在此期间收缩,则会失败; PyObject_GetAttr即使在PyObject_HasAttr成功之后,在某些情况下也会失败.
这是一个重写(但未经测试)的代码版本,具有以下更改:
效用函数从定义它的模块中GetExpressionTreeClass获取ExpressionTree类.(填写正确的模块名称my_module.)
Guard是一个RAII风格的防护类,在离开范围时释放Python对象.这个小而简单的类使引用计数异常安全,其构造函数本身处理NULL对象.boost::python定义此样式的功能层,我建议您查看它.
所有Python_exception抛出现在伴随着设置Python异常信息.因此捕手Python_exception可以使用PyErr_PrintExc或PyErr_Fetch打印异常或以其他方式找出问题所在.
代码:
class Guard {
PyObject *obj;
public:
Guard(PyObject *obj_): obj(obj_) {
if (!obj)
throw Python_exception("NULL object");
}
~Guard() {
Py_DECREF(obj);
}
};
PyObject *GetExpressionTreeClass()
{
PyObject *module = PyImport_ImportModule("my_module");
Guard module_guard(module);
return PyObject_GetAttrString(module, "ExpressionTree");
}
void VisitTree(PyObject* py_tree) throw (Python_exception)
{
PyObject *cls = GetExpressionTreeClass();
Guard cls_guard(cls);
PyObject* list = PyObject_GetAttrString(py_tree, "children");
if (!list && PyErr_ExceptionMatches(PyExc_AttributeError)) {
PyErr_Clear(); // hasattr does this exact check
return;
}
Guard list_guard(list);
Py_ssize_t size = PyList_Size(list);
for (Py_ssize_t i = 0; i < size; i++) {
PyObject* child = PyList_GetItem(list, i);
Py_XINCREF(child);
Guard child_guard(child);
// check if child is class instance or number (terminal)
if (PyInt_Check(child) || PyLong_Check(child) || PyString_Check(child))
; // terminal - do nothing for now
else if (PyObject_IsInstance(child, cls))
VisitTree(child);
else {
PyErr_Format(PyExc_TypeError, "unrecognized %s object", Py_TYPE(child)->tp_name);
throw Python_exception("unrecognized object from python");
}
}
}
Run Code Online (Sandbox Code Playgroud)