Mat*_*sen 6 python python-c-api python-2.7
假设我有一个如下所示的人为函数:
静态 int foo(PyObject *self, PyObject *args) {
字符 *a = "";
字符 *b = "";
整数c=0;
if (!PyArg_ParseTuple(args, "ss|i", &a, &b, &c) {
返回空值;
}
printf("c 是 %i\n", c);
//some_function_requiring_int_data_type(c);
}
我希望用户能够将 anint或 aNone作为值提交给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
Run Code Online (Sandbox Code Playgroud)
目前,为了启用此行为,我有一堆丑陋的代码,如下所示:
静态 int foo(PyObject *self, PyObject *args) {
字符 *a = "";
字符 *b = "";
PyObject *c = NULL; // 注意我如何使用 PyObject *
int c_int = 0; // 注意我如何有一个伴随的 int
if (!PyArg_ParseTuple(args, "ss|O", &a, &b, &c) {
返回空值;
}
// 丑陋的代码从这里开始
如果(c){
如果(c!= Py_None){
如果(!PyInt_Check(c)){
PyErr_SetString(PyExc_TypeError, "c 必须是 int 或 None");
返回;
}
c_int = PyInt_AsSize_t(c);
}
}
printf("c_int 是 %i\n", 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
Run Code Online (Sandbox Code Playgroud)
我的第一个建议是仅使用关键字参数。这样做的主要优点是避免必须传递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);
}
Run Code Online (Sandbox Code Playgroud)
(在 Python 3 中,您可以删除长度检查并仅用于"ss|$i"指定 are 关键字后面的参数$,这更好一些)。您需要将函数类型指定为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
Run Code Online (Sandbox Code Playgroud)
但不是
int_from_kw("something","something else",c="not an int")
int_from_kw("something","something else",5)
Run Code Online (Sandbox Code Playgroud)
缺点是该方法并不总是有效 - 有时您需要该函数符合第三方库强制执行的固定接口。
我的第二个建议是使用转换器函数。这并没有消除任何样板,而是将其全部保存在一个封闭且可重复使用的地方。这里的版本适用于 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);
}
Run Code Online (Sandbox Code Playgroud)
一些简短的说明(参考您的版本):
o永远不会发生NULL,因为它来自 Python,它总是会给你一个对象。None我们不改变指针。这允许在调用函数中设置默认值。这些建议都没有真正回答所提出的问题,但它们确实提供了我认为更清洁的替代方案。
| 归档时间: |
|
| 查看次数: |
1056 次 |
| 最近记录: |