代码之家  ›  专栏  ›  技术社区  ›  hoefling

接受可选整数的Python C扩展函数

  •  0
  • hoefling  · 技术社区  · 4 年前

    我想在C扩展模块中实现以下Python函数:

    def value(x: Optional[int] = None) -> Optional[int]:
        if x is None:
            # act like a getter
            return APIGetValue()  # retrieve the value from an external library
        # act like a setter
        APISetValue(x)  # pass the value to an external library
        return None
    

    到目前为止,我得到了以下结果:

    static PyObject* MyLib_PyValue(PyObject *self, PyObject *args, PyObject *kwargs) {
        static char *kwlist[] = { "x", NULL };
        int x;
        if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i:value", kwlist, &x)) {
            return NULL;
        }
        if (x == NULL) {
            return PyLong_FromLong(APIGetValue());
        }
        APISetValue(x);
        Py_RETURN_NONE;
    }
    

    使用args调用函数是有效的,但在调用时 value() 作为一个吸气剂,我得到 1 而不是 NULL 。我该如何继续?我对Python的C API还不是很熟悉。

    0 回复  |  直到 4 年前
        1
  •  3
  •   user2357112    4 年前

    首先,你不能有NULL int。NULL是指针的东西。由于C类型转换的工作方式以及 NULL 宏被定义, x == NULL 使用int x 通常执行以下两种操作之一: x == 0 ,否则会产生编译时错误。

    第二,引用 Python C API argument parsing docs ,

    当未指定可选参数时,与可选参数对应的C变量应初始化为默认值,PyArg_ParseTuple()不会触及相应C变量的内容。

    这也适用于 PyArg_ParseTupleAndKeywords .你的 x 未初始化,以及 PyArg_解析表达式和关键字 不为其写入值,因此访问 x x==空 比较是一种未定义的行为。

    您需要初始化 x ,并且您需要使用一种实际上允许您检测缺失值的类型。这可能意味着宣布 x 作为 PyObject *x = NULL; 并通过 "|O:value" PyArg_解析表达式和关键字 ,然后在函数内部处理到C long的转换,而不是依赖于 PyArg_解析表达式和关键字 去做。

        2
  •  0
  •   hoefling    4 年前

    这是最终结果,以防有人需要一个工作片段:

    static PyObject* MyLib_PyValue(PyObject *self, PyObject *args, PyObject *kwargs) {
        static char *kwlist[] = { "x", NULL };
        PyObject *pyX = NULL;
        if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O:value", kwlist, &pyX)) {
            return NULL;
        }
        if (pyX == NULL) { // getter
            return PyLong_FromLong(APIGetValue());
        }
        // setter
        int x = (int)PyLong_AsLong(pyX);
        if (PyErr_Occurred()) {
            return NULL;
        }
        if (x < 0) {
            PyErr_SetString(PyExc_ValueError, "x must be positive");
            return NULL;
        }
        APISetValue(x);
        Py_RETURN_NONE;
    }