使用 C API 中的 *args 和 **kwargs 调用 Python 函数
Call Python function with *args and **kwargs from C API
我正在编写一些 C 级函数,它应该接受一个可调用对象 *args
和 **kwargs
,打印一些内容并使用参数调用可调用对象。它的签名应该反映出这个来自 Python:
def my_function(callable, *args, **kwargs): ...
这是我的 C 代码:
static PyObject *call_c(PyObject *self, PyObject *args, PyObject *keywords) {
PyObject *f;
PyObject *arguments;
if (!PyArg_ParseTuple(args, "O|O", &f, &arguments))
{
PyErr_SetString(PyExc_ValueError, "invalid args");
return NULL;
}
printf("Calling...\n");
return PyObject_Call(f, arguments, keywords);
}
以下是我尝试执行它的方式:
def printer(value="test"):
print(f"printed: {value}")
def call_c_wrapper(callable, *args, **kwargs):
call_c(callable, args, **kwargs)
call_c(print, "some print value")
# Calling...
# Segmentation fault
call_c(printer, value="test")
# Traceback (most recent call last):
# File "main.py", line 35, in <module>
# call_c(printer, value="test")
# TypeError: call_c() takes no keyword arguments
call_c_wrapper(print, "some print value")
# > this is the only one that works:
# Calling...
# some print value
call_c_wrapper(printer, value="my value")
# > same as `call_c` with kwargs:
# Traceback (most recent call last):
# File "main.py", line 37, in <module>
# call_c_wrapper(printer, value="my value")
# File "main.py", line 32, in call_c_wrapper
# call_c(callable, args, **kwargs)
# TypeError: call_c() takes no keyword arguments
我的代码有什么问题? *args
解析似乎有些工作,如果有必要,我可以使用包装函数,但为什么 **kwargs
根本不起作用?
编辑
我已将 PyMethodDef
从 METH_VARARGS
更改为 METH_VARARGS | METH_KEYWORDS
,因此 call_c_wrapper()
现在可以正常工作了。
args
的call_c
仍然给我SegmentationFault
,kwargs
的加注MemoryError
.
正如我在 the comments to your now-deleted earlier version of this question 中所建议的那样:我认为 PyArg_ParseTuple
是错误的工具,因为它实际上没有处理 *args
.
的机制
你想要做的是类似于下面的Python代码:
def f(*args, **kwds):
f = args[0]
args = args[1:]
return f(*args, **kwds)
最简单的方法就是使用 PyTuple_...
API (https://docs.python.org/3/c-api/tuple.html).
大致如下:
static PyObject *call_c(PyObject *self, PyObject *args, PyObject *keywords) {
PyObject *f;
PyObject *arguments;
if (PyTuple_Size(args) == 0)
{
PyErr_SetString(PyExc_ValueError, "Missing argument 'f'");
return NULL;
}
f = PyTuple_GET_ITEM(args, 0); // Note: borrowed reference so no reference counting needed
arguments = PyTuple_GetSlice(args, 1, PyTuple_Size(args));
// ideally you should check arguments isn't NULL too
printf("Calling...\n");
PyObject* result = PyObject_Call(f, arguments, keywords);
Py_XDECREF(arguments);
return result;
}
我正在编写一些 C 级函数,它应该接受一个可调用对象 *args
和 **kwargs
,打印一些内容并使用参数调用可调用对象。它的签名应该反映出这个来自 Python:
def my_function(callable, *args, **kwargs): ...
这是我的 C 代码:
static PyObject *call_c(PyObject *self, PyObject *args, PyObject *keywords) {
PyObject *f;
PyObject *arguments;
if (!PyArg_ParseTuple(args, "O|O", &f, &arguments))
{
PyErr_SetString(PyExc_ValueError, "invalid args");
return NULL;
}
printf("Calling...\n");
return PyObject_Call(f, arguments, keywords);
}
以下是我尝试执行它的方式:
def printer(value="test"):
print(f"printed: {value}")
def call_c_wrapper(callable, *args, **kwargs):
call_c(callable, args, **kwargs)
call_c(print, "some print value")
# Calling...
# Segmentation fault
call_c(printer, value="test")
# Traceback (most recent call last):
# File "main.py", line 35, in <module>
# call_c(printer, value="test")
# TypeError: call_c() takes no keyword arguments
call_c_wrapper(print, "some print value")
# > this is the only one that works:
# Calling...
# some print value
call_c_wrapper(printer, value="my value")
# > same as `call_c` with kwargs:
# Traceback (most recent call last):
# File "main.py", line 37, in <module>
# call_c_wrapper(printer, value="my value")
# File "main.py", line 32, in call_c_wrapper
# call_c(callable, args, **kwargs)
# TypeError: call_c() takes no keyword arguments
我的代码有什么问题? *args
解析似乎有些工作,如果有必要,我可以使用包装函数,但为什么 **kwargs
根本不起作用?
编辑
我已将 PyMethodDef
从 METH_VARARGS
更改为 METH_VARARGS | METH_KEYWORDS
,因此 call_c_wrapper()
现在可以正常工作了。
args
的call_c
仍然给我SegmentationFault
,kwargs
的加注MemoryError
.
正如我在 the comments to your now-deleted earlier version of this question 中所建议的那样:我认为 PyArg_ParseTuple
是错误的工具,因为它实际上没有处理 *args
.
你想要做的是类似于下面的Python代码:
def f(*args, **kwds):
f = args[0]
args = args[1:]
return f(*args, **kwds)
最简单的方法就是使用 PyTuple_...
API (https://docs.python.org/3/c-api/tuple.html).
大致如下:
static PyObject *call_c(PyObject *self, PyObject *args, PyObject *keywords) {
PyObject *f;
PyObject *arguments;
if (PyTuple_Size(args) == 0)
{
PyErr_SetString(PyExc_ValueError, "Missing argument 'f'");
return NULL;
}
f = PyTuple_GET_ITEM(args, 0); // Note: borrowed reference so no reference counting needed
arguments = PyTuple_GetSlice(args, 1, PyTuple_Size(args));
// ideally you should check arguments isn't NULL too
printf("Calling...\n");
PyObject* result = PyObject_Call(f, arguments, keywords);
Py_XDECREF(arguments);
return result;
}