Преобразовать итерируемый объект в кортеж в python C api - PullRequest
0 голосов
/ 10 апреля 2020

У меня есть итеративная PyObject, которую мне нужно передать в качестве списка аргументов для вызываемого Python, ala

xs = [1,2,5,7]
some_function(*xs)

Однако, PyObject_CallObject позволяет передавать только кортежи как контейнер аргументов.

Я не уверен, однако, каков наилучший способ создать новый кортеж, содержащий элементы итерируемого. Вот предварительный код:

PyObject* unpack_call(PyObject* callable, PyObject* args) {
  PyObject* tuple;
  if (PyTuple_Check(args)) {
    tuple = args;
  } else {
    PyObject* iter = PyObject_GetIter(args);
    if (!iter) return NULL;

    tuple = PyTuple_New(0);

    for (Py_ssize_t pos=0; ; ++pos) {
      PyObject* arg = PyIter_Next(iter);
      if (!arg) break;

      PyTuple_SetItem(tuple,pos,arg);
    }
  }
  return PyObject_CallObject(callable,tuple);
}

Я не уверен, нужно ли мне самому увеличивать размер кортежа или нет. Что меня смущает, так это предложение в документации , говорящее:

Кортеж всегда будет расти или уменьшаться в конце.

Я тоже не уверен, что мне нужно увеличить или уменьшить счетчик ссылок для любого из этих объектов.

1 Ответ

1 голос
/ 10 апреля 2020

Вы намного лучше используете PySequence_Tuple, который может создать кортеж непосредственно из итерируемого. Вероятно, есть и другие подобные варианты, чтобы сделать это тоже.

Если вы действительно хотите сделать это по-своему, вам нужно каждый раз вызывать PyTuple_Resize, чтобы добавить его (или, возможно, изменить его размер кусками). Что предложение

Кортеж всегда будет увеличиваться или уменьшаться в конце.

говорит вам, что в конце кортежа есть дополнительное пространство.

PyTuple_SetItem крадет ссылку, поэтому вам не нужно прикасаться к счетчику ссылок добавляемых вами элементов. Вы должны сделать некоторую проверку ошибок, хотя - PyIter_Next может вызвать исключение. Вам также нужно определить iter и кортеж, когда вы закончите с ними.

...