Python C API: вызов функции Python с аргументом (ами), переданными по ссылке

1

Я пишу модуль python в C, и мне нужно, чтобы он вызывал функцию Python с одним из аргументов, переданных "ссылкой". Конечным результатом должно быть то, что функция 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. Любые предложения?

Теги:
python-c-api

1 ответ

4
Лучший ответ

Я думаю, вы неправильно понимаете, как работают переменные в 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.

  • 0
    Я понимаю, что (я думаю) я предположил, что, поскольку * resumeInt ссылается на тот же объект, после передачи его в PyObject_CallFunction у меня останется новый * resumeInt, модифицированный вызываемой функцией Python. Затем я просто конвертирую его обратно в переменную C с помощью PyInt_AsLong. Что-то не так с этой идеей? Ваше предложение звучит несколько элегантнее и проще, но я бы хотел это понять.
  • 0
    Ах, после вашего редактирования это имеет больше смысла. Поэтому то, что я пытаюсь сделать, возможно, будет невозможно. Я изменю код, чтобы он возвращал значение. Я бы проголосовал, но мне не хватает репутации; отличный ответ, спасибо большое!

Ещё вопросы

Сообщество Overcoder
Наверх
Меню