使用C扩展python:将列表传递给PyArg_ParseTuple

Joh*_*hnJ 10 c python python-c-api python-c-extension

我一直在尝试用C扩展python,到目前为止,基于文档,我在编写小型C函数和使用Python扩展它方面取得了相当大的成功.

但是,我现在遇到了一个相当简单的问题 - 我无法找到解决方案.所以,我想做的是传递double list给我的C函数.例如,要传递int,我执行以下操作:

int squared(int n)
{
    if (n > 0)
        return n*n;
    else
        return 0;
}

static PyObject*
squaredfunc(PyObject* self, PyObject* args)
{
    int n;

    if (!PyArg_ParseTuple(args, "i", &n))
        return NULL;

    return Py_BuildValue("i", squared(n));
}
Run Code Online (Sandbox Code Playgroud)

这将int n没有任何问题传递给我的C函数命名squared.

但是,如何将一个传递list给C函数?我确实尝试谷歌并阅读文档,到目前为止,我还没有找到任何有用的东西.

如果有人能指出我正确的方向,我真的很感激.

谢谢.

Nat*_*ert 15

PyArg_ParseTuple只能处理简单的C类型,复数,char *,PyStringObject *,PyUnicodeObject *,和PyObject *.使用PyListObject的唯一方法是使用"O"的某个变体并将对象解压缩为PyObject*.然后,您可以使用List Object API检查该对象是否确实是一个list(PyList_Check).然后,您可以使用PyList_SizePyList_GetItem迭代列表.请注意,在迭代时,您将获得PyObject *并将必须使用浮点API来访问实际值(通过执行PyFloat_CheckPyFloat_AsDouble.)作为List API的替代方法,您可以更灵活并使用迭代器协议(其中)你应该只使用PyIter_Check).这将允许您迭代支持迭代器协议的任何内容,如列表,元组,集等.

最后,如果你真的希望你的函数接受double n[]并且你想避免所有的手动转换,那么你应该使用像boost :: python这样的东西.学习曲线和API更复杂,但boost :: python将自动处理所有转换.

下面是使用迭代器协议进行循环的示例(这是未经测试的,您需要填写错误处理代码):

PyObject *obj;

if (!PyArg_ParseTuple(args, "O", &obj)) {
  // error
}

PyObject *iter = PyObject_GetIter(obj);
if (!iter) {
  // error not iterator
}

while (true) {
  PyObject *next = PyIter_Next(iter);
  if (!next) {
    // nothing left in the iterator
    break;
  }

  if (!PyFloat_Check(next)) {
    // error, we were expecting a floating point value
  }

  double foo = PyFloat_AsDouble(next);
  // do something with foo
}
Run Code Online (Sandbox Code Playgroud)


Mat*_*t P 9

PyArg_ParseTuple功能使您可以直接转换为使用格式字符串Python对象亚型"O!"(注意,这是不同于只是普通的"O").如果参数与指定的PyObject类型不匹配,则会抛出TypeError.例如:

PyObject *pList;
PyObject *pItem;
Py_ssize_t n;
int i;

if (!PyArg_ParseTuple(args, "O!", &PyList_Type, &pList)) {
    PyErr_SetString(PyExc_TypeError, "parameter must be a list.");
    return NULL;
}

n = PyList_Size(pList);
for (i=0; i<n; i++) {
    pItem = PyList_GetItem(pList, i);
    if(!PyInt_Check(pItem)) {
        PyErr_SetString(PyExc_TypeError, "list items must be integers.");
        return NULL;
    }
}
Run Code Online (Sandbox Code Playgroud)

作为旁注,请记住,使用PyList_GetItem返回对每个项目的借用引用来迭代列表,因此您不需要Py_DECREF(item)处理引用计数.另一方面,使用有用的迭代器协议(参见@NathanBinkert 的答案),返回的每个项目都是一个新的引用 - 所以你必须记住在完成使用时丢弃它Py_DECREF(item).