Python C API: вызов функции Python с аргументом (ами), переданными по ссылке - PullRequest
1 голос
/ 03 декабря 2010

Я пишу модуль Python на C, и мне нужно, чтобы он вызывал функцию Python с одним из аргументов, передаваемых «reference».Конечный результат должен состоять в том, что то, что функция Python делает с аргументом, сохраняется в исходной переменной C.

int      *resume;      // this int is actually passed in to this body of code
PyObject *resumeInt;   // which was originally a C callback func for libnids, but
PyObject *ret;         // I removed/rewrote most of this code for clarity

resumeInt = Py_BuildValue("i",-1);
ret = PyObject_CallFunction(tcpResumeFunc, "(O)", resumeInt);    

*resume = PyInt_AsLong(resumeInt);
Py_DECREF(ret);
Py_DECREF(resumeInt);

Для тестирования у меня была функция Python, которую представляет tcpResumeFunc, изменив переданное целое число до5. Однако, когда я печатаю * резюме в конце этого кода, оно сохраняет свое начальное значение -1.Я знаю, что неправильно понимаю, как работает API.Есть предложения?

1 Ответ

4 голосов
/ 03 декабря 2010

Я думаю, вы неправильно понимаете, как переменные работают в Python. Переменные в Python не содержат значений; они ссылаются на них - как в Java, за исключением того, что нет никаких «примитивов» (даже int). Присвоение переменной не перезаписывает старое значение; это просто приводит к тому, что переменная ссылается на новое значение и перестает ссылаться на старое (которое затем может быть собрано мусором, если на него больше ничего не ссылается).

Почему бы просто не вернуть (и не использовать) значение (поскольку функции Python в любом случае всегда возвращают что-то, даже если это просто неявное возвращение None)?


Изменить: Работа с примером, потому что это слишком долго для ответа на комментарий.

Из C мы вызываем PyObject_CallFunction, которая передает PyObject * функции Python. В Python параметр функции (назовем его spam) теперь ссылается на указанный объект PyObject.

Когда мы пишем spam += 6 в функции, это совпадает с spam = spam + 6. Интерпретатор байт-кода получает PyObject, представляющий значение 6, запускает специальный байт-код, который проверяет значение, представленное двумя объектами, добавляет их и создает новый PyObject (или получает один из кэша), представляющий сумму.

Затем spam возвращается к новому объекту; но ссылка на переменную spam находится на стороне Python забора памяти и не совпадает с PyObject * на стороне C забора.

Таким образом, resumeInt делает не указанным на недавно созданный / извлеченный PyObject.

На самом деле он ведет себя точно так же, как при вызове функции Python из Python. Попробуйте:

def fry(spam):
  spam += 1

eggs = 3
fry(eggs)
eggs # still 3!

Это происходит потому, что повторное связывание spam не влияет на eggs, который является отдельной переменной. Мы не передаем по ссылке; мы передаем a ссылку на значение . Так же, как в Java.

...