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

问题描述 投票:0回答:1

我想在一个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还不是很熟悉。

python c python-c-api
1个回答
1
投票

首先,你不能有一个NULL的int。NULL是指针的东西。由于C类型转换的工作方式和 NULL 宏被定义。x == NULL 带着int x 通常有两种情况:它表现为 x == 0,否则会产生编译时错误。

第二,引用 Python C API参数解析文档,

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

这也适用于 PyArg_ParseTupleAndKeywords. 您的 x 是未初始化的,并且 PyArg_ParseTupleAndKeywords 不会为它写一个值,所以访问 xx == NULL 比较是未定义的行为。

您需要初始化 x而你需要使用一个能让你真正检测到缺失值的类型。这可能意味着你要声明 x 作为 PyObject *x = NULL; 并通过 "|O:value"PyArg_ParseTupleAndKeywords,然后在你的函数中处理转换为C长,而不是依靠 PyArg_ParseTupleAndKeywords 来完成它。


0
投票

这就是最终的结果,如果有人需要一个可行的片段。

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;
}
© www.soinside.com 2019 - 2024. All rights reserved.