python 函数的参数为none_如何允许None或特定类型作为Python C Extension函数的参数?...

假设我有如下的人为功能:

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

", c);

//some_function_requiring_int_data_type(c);

}

我希望用户能够将int或None作为值提交给carg,但上述代码不允许这样做:

>>>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)) {

PyExc_SetString(PyExc_TypeError,"c must be int or None");

return;

}

c_int = PyInt_AsSsize_t(c);

}

}

printf("c_int is %i

", c_int);

//some_function_requiring_int_data_type(c_int);

}

及其用途:

>>>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作为占位符值传递)。如果失败,我将使用"o&"和转换器功能。这有同样多的丑陋的代码,但至少在一个明确的独立单元。(如果有帮助,我可以发布代码,但应该很容易理解)。不幸的是,使用C API很难完全避免丑陋的代码。

@大卫,谢谢你给我的小费。你能看看我的答案吗?我有点担心。此外,是否要发布建议使用仅关键字参数作为答案?

我已经发布了转换器函数的版本。与您的主要区别在于,它不会更改EDOCX1上的任何内容(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

", 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)

缺点是这种方法并不总是有效——有时您需要该函数符合第三方库强制执行的固定接口。

我的第二个建议是使用转换器函数。这并没有消除任何锅炉板,但保持在一个良好的遏制和可重复使用的地方。这里的版本是针对python 3的(因为我已经安装了它!)但我认为python 2的主要变化是用PyInt替换PyLong。

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

", c);

return PyLong_FromSsize_t(c);

}

一些简要说明(参考您的版本):

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

这些建议都没有真正回答问题,但它们确实提供了我认为更清洁的选择。

使用转换器功能是一种方法。谢谢@davidw给你小费。

我确实有一些问题:

百万千克1如果我没有输入正确的数据类型,我现在可能会导致segfaults。百万千克1百万千克1它要求在为空时,int值只能是一个值(在本例中为0)。它不能被通用化,在不同的情况下,我需要默认值(如-1)百万千克1百万千克1我必须对异常消息进行硬编码("c必须是int"),因此我不能对其他变量重复使用它。百万千克1

如果有人在附近工作,请将其作为答案张贴。

static int int_or_none(PyObject *python, void *c) {

int temp = 0;

if (python) {

if (python != PyNone) {

if (!PyInt_Check(python)) {

PyErr_SetString(PyExc_TypeError,"c must be int");

return 0;

}

tmp = PyInt_AsSsize_t(python);

if (tmp  0, not %i", tmp);

return 0;

}

}

}

*((int *) c) = tmp;

return 0;

}

static int foo(PyObject *self, PyObject *args) {

char *a ="";

char *b ="";

int *c = NULL; // If I accidentally make this a char *c, it may segfault

if (!PyArg_ParseTuple(args,"ss|O&", &a, &b, &int_or_none, &c) {

return NULL;

}

printf("c_int is %i

", c_int);

//some_function_requiring_int_data_type(c_int);

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值