gpt4 book ai didi

python - 如何允许 None 或特定类型作为 Python C 扩展函数的参数?

转载 作者:太空宇宙 更新时间:2023-11-03 15:57:42 25 4
gpt4 key购买 nike

假设我有一个人为设计的函数,如下所示:

static int foo(PyObject *self, PyObject *args) {    char *a = "";    char *b = "";    int c = 0;    if (!PyArg_ParseTuple(args, "ss|i", &a, &b, &c) {        return NULL;    }    printf("c is %i\n", c);    //some_function_requiring_int_data_type(c);}

I would like the user to be able to submit either an int or a None as the value to the c arg, but the above code doesn't allow this:

>>>from spam import foo
>>>foo('a', 'b')
c is 0
>>>foo('a', 'b', 100)
c is 100
>>>foo('a', 'b', None)
TypeError: an integer is required

目前,为了启用此行为,我有一堆丑陋的代码,如下所示:

static int foo(PyObject *self, PyObject *args) {    char *a = "";    char *b = "";    PyObject *c = NULL; // Note how I use PyObject *    int c_int = 0; // Note how I have an accompanying int    if (!PyArg_ParseTuple(args, "ss|O", &a, &b, &c) {        return NULL;    }    // Ugly code starts here    if (c) {        if (c != Py_None) {            if (!PyInt_Check(c)) {                PyErr_SetString(PyExc_TypeError, "c must be int or None");                return;            }            c_int = PyInt_AsSize_t(c);        }    }    printf("c_int is %i\n", c_int);    //some_function_requiring_int_data_type(c_int);}

And its use:

>>>from spam import foo
>>>foo('a', 'b')
c is 0
>>>foo('a', 'b', 100)
c is 100
>>>foo('a', 'b', None)
c is 0

最佳答案

我的第一个建议是只使用关键字参数。这样做的主要优点是避免必须传递 None 占位符值,因为您永远不必“填写”(比方说)未指定的第三个位置参数,以便指定第四个。它基本上改变了 Python 界面,以更“符合您的意思”。

static PyObject* int_from_kw(PyObject* self, PyObject* args, PyObject* kwargs) {
char *a, *b;
Py_ssize_t c = 0; // default value

char* kwarg_names[] = {"a","b","c",NULL};

// optional check to ensure c is passed only as a keyword argument - not needed with Python 3
if (PyTuple_Size(args)>2) {
PyErr_SetString(PyExc_TypeError,"Only two positional arguments allowed");
return NULL;
}

if (!PyArg_ParseTupleAndKeywords(args,kwargs,"ss|i",kwarg_names,&a,&b,&c)) {
return NULL;
}
printf("c_int is %li\n", c);
return PyLong_FromSsize_t(c);
}

(在 Python 3 中,您可以删除长度检查并使用 "ss|$i" 指定 $ 之后的参数仅为关键字,这有点更好)。您需要将函数类型指定为 METH_VARARGS|METH_KEYWORDS

然后您可以从 Python 中调用它

int_from_kw("something","something else") # default c
int_from_kw("something","something else",c=5)
int_from_kw(a="something",b="something else",c=5) # etc

但不是

int_from_kw("something","something else",c="not an int")
int_from_kw("something","something else",5)

缺点是该方法并不总是有效 - 有时您需要函数符合第 3 方库强制执行的固定接口(interface)。


我的第二个建议是使用转换器函数。这并没有消除任何样板,而是将其全部保存在一个包含良好且可重复使用的地方。这里的版本适用于 Python 3(因为这是我安装的!),但我认为 Python 2 的主要变化是将 PyLong 替换为 PyInt

int int_or_none(PyObject* o, void* i) {
Py_ssize_t tmp;
Py_ssize_t* i2 = i;
if (o==Py_None) {
return 1; // happy - leave integer as the default
}
if (PyLong_Check(o)) {
tmp = PyLong_AsSize_t(o);
if (PyErr_Occurred()) {
return 0;
} else {
*i2 = tmp;
return 1;
}
}
PyErr_SetString(PyExc_TypeError, "c must be int or None");
return 0; // conversion failed
}

static PyObject* test_int_none(PyObject* self, PyObject* args) {
char *a, *b;
Py_ssize_t c = 0; // default value

if (!PyArg_ParseTuple(args, "ss|O&", &a, &b, int_or_none, &c)) {
return NULL;
}
printf("c_int is %i\n", c);
return PyLong_FromSsize_t(c);
}

一些简短的说明(引用您的版本):

  • 我们确信 o 永远不会是 NULL,因为它来自 Python,它总是会给你一个对象。
  • 如果发生故障或None,我们不会更改指针。这允许在调用函数中设置默认值。
  • 转换为 C 整数类型后,我们必须检查是否发生错误,因为如果整数太大,可能会出现溢出错误。在这种情况下,已经设置了正确的异常,所以我们只需要返回 0 来指示失败。 (我认为这与 Python 2 无关,因为它使用单独的大整数和小整数类型)

这些建议都没有真正回答所提出的问题,但它们确实提供了我认为更清洁的替代方案。

关于python - 如何允许 None 或特定类型作为 Python C 扩展函数的参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42333354/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com