Python C扩展中的True * args和** kwargs

True *args and **kwargs in Python C extension

我正在开发python 3 c扩展。

我可以得到等价的或任意的位置或关键字参数吗?

例如,在python中,我可以写:

1
2
3
4
def fun(name, parent, *args, **kwargs):
    # do something with name and parent
    # do something with args and kwargs
    pass

但是我在C语言中找不到一个简单的等价物。虽然我们可以用PyObject* argsPyObject* kwargs完美地编写函数,但我不能轻易地"解析"名称和父级,无论它来自哪个(args/kwargs)。

采取:

1
2
3
4
5
6
7
8
9
10
11
static PyObject* myFunction(PyObject* self, PyObject* args, PyObject* kwargs) {
    char* kwds[] = {"parent","name", NULL};
    PyObject* name = NULL;
    PyObject* parent = NULL;
    if (!PyArg_ParseTupleAndKeywords(args, kwargs,"OO", kwds, &parent, &name)) {
        goto errorParseTupleAndKeywords;
    }
    /* Do something with name and parent */
    /* parent and name maybe have appeared either in args or kwargs */
    /* But I don't have any extra positional (*args) or keyword (**kwargs) here */    
}

我能想到的"手动"方法大致如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static PyObject* myFunction(PyObject* self, PyObject* args, PyObject* kwargs) {
    PyObject* name = NULL;
    PyObject* parent = NULL;
    int inKwargs = 0;
    // Pretend to do something with parent
    if (PyDict_GetItemString(kwargs,"parent")) {
        inKwargs++;
        PyDict_DelItemString(kwargs,"parent");
    }
    // Pretend to do something with name
    if (PyDict_GetItemString(kwargs,"name")) {
        inKwargs++;
        PyDict_DelItemString(kwargs,"name");
    }
    // Not sure if -1 works here
    PyObject* newArgs = PyTuple_GetSlice(args, inKwargs, -1); // this is *args
    // the remaining kwargs can be used as **kwargs
}


在C API中,PyObject* args实际上是一个python元组,而PyObject* kwargs实际上是一个python字典。至少这是PyArg_ParseTupleAndKeywords内部要求的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int PyArg_ParseTupleAndKeywords(PyObject *args, PyObject *keywords, const char *format, char **kwlist, ...)
{
    // …
    if ((args == NULL || !PyTuple_Check(args)) ||
        (keywords != NULL && !PyDict_Check(keywords)) ||
        format == NULL ||
        kwlist == NULL)
    {
        PyErr_BadInternalCall();
        return 0;
    }

    // …
}

该函数在vgetargskeywords中的实际实现也再次声明了这一点,因此您可以用从对象中手动提取来替换PyArg_ParseTupleAndKeywords调用。

这意味着您可以同时使用元组和dict API,或者使用迭代器协议对这些对象中的项进行迭代。