首页 > 解决方案 > 接受可选整数的 Python C 扩展函数

问题描述

我想在 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()作为 getter 调用时,我得到1的是NULL. 我应该如何进行?我对 Python 的 C API 还不是很熟悉。

标签: pythoncpython-c-api

解决方案


首先,你不能有一个 NULL int。NULL 是指针的东西。由于 C 类型转换的工作方式以及NULL宏的定义方式,x == NULL使用 intx通常会做以下两件事之一:它表现为x == 0,或者它会产生编译时错误。

其次,引用Python C API 参数解析文档

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

这也是如此PyArg_ParseTupleAndKeywords。Yourx未初始化,并且PyArg_ParseTupleAndKeywords没有为其写入值,因此访问比较中的值xx == NULL未定义的行为。

您需要初始化x,并且需要使用一种实际上允许您检测缺失值的类型。这可能意味着声明xasPyObject *x = NULL;并传递"|O:value"to PyArg_ParseTupleAndKeywords,然后在函数内部处理到 C long 的转换,而不是依赖于PyArg_ParseTupleAndKeywords来完成它。


推荐阅读