Python拓展开发

发表时间: 2020-05-24 22:14:19 作者: 何景松 7 390

    前些日子写了一个序列化工具,想着为这个工具写一个python拓展。写这篇文章就是为了记录一下写python拓展时遇到的坑。

    python版本:Python-3.7.5

    1、未减少引用计数问题

    static void getData(const void *pyObject, sg_UserData *ud)
    {
        PyObject* object = (PyObject*)pyObject;
        PyObject *key = NULL;
        PyObject *value = NULL;
    
        key = PyUnicode_FromString(fieldName);
        value = PyDict_GetItem(object, key);
    RET:
        Py_XDECREF(key);
        return;
    }
    

    这里的key用完一定得释放,因为这里的key只是为了获取到value值而已。

    2、对Python API了解不足

    static void *setData(void *pyObject, sg_UserData *ud)
    {
        PyObject *key = NULL;
        PyObject *object = (PyObject *)pyObject;
        PyObject *value = NULL;
    
        value = PyList_New(ud->len);
        PyList_SetItem(object, ud->idx, value);
    
        Py_XDECREF(value);
        Py_XDECREF(key);
    }
    

    这里虽然减少了引用计数,但是在python调用object里面数据的时候会报错的。因为PyList_SetItem这个函数不会增加value的计数。

    #define Py_XSETREF(op, op2)                     \
        do {                                        \
            PyObject *_py_tmp = (PyObject *)(op);   \
            (op) = (op2);                           \
            Py_XDECREF(_py_tmp);                    \
        } while (0)
    
    int
    PyList_SetItem(PyObject *op, Py_ssize_t i, PyObject *newitem)
    {
        PyObject **p;
        // 一些判断
        p = ((PyListObject *)op) -> ob_item + i;
        Py_XSETREF(*p, newitem);
        return 0;
    }
    

    由源码可见,PyList_SetItem仅仅是改变ob_item内元素的指向而已,而不像PyDict_SetItem这类函数会增加value的计数值。至于python为什么这么做,我也不清楚了。
    所以正确的写法是:

    static void *setData(void *pyObject, sg_UserData *ud)
    {
        PyObject *key = NULL;
        PyObject *object = (PyObject *)pyObject;
        PyObject *value = NULL;
    
        value = PyList_New(ud->len);
        Py_XINCREF(value);	// <-- 增加这个
        PyList_SetItem(object, ud->idx, value);
    
        Py_XDECREF(value);
        Py_XDECREF(key);
    }