Страницы

Поиск по вопросам

вторник, 23 октября 2018 г.

Как узнать в Си расширении, сколько всего аргументов передано в Питон функцию, все их просуммировать и вернуть результат?

Привет. Самообучаюсь написанию модулей на C для Python по этому адресу https://docs.python.org/3/c-api/arg.html (скоро по работе может пригодиться).
Легко написал пример модуля с функцией add, которая суммирует два целых числа.
Как получить количество аргументов, переданных в функцию, чтобы она все их просуммировала и вернула результат? Что и как изменить в коде?
mymod.c
#include
static PyObject * mymod_add(PyObject *self, PyObject *args) { int a = 0, b = 0; PyArg_ParseTuple(args, "i|i", &a, &b); return Py_BuildValue("i", a + b); }
static PyMethodDef mymod_methods[] = { {"add", mymod_add, METH_VARARGS, "The method adds two ints."}, {NULL, NULL, 0, NULL} };
static struct PyModuleDef mymod_definition = { PyModuleDef_HEAD_INIT, "mymod", "My test Python module.", -1, mymod_methods };
PyMODINIT_FUNC PyInit_mymod(void) { Py_Initialize(); return PyModule_Create(&mymod_definition); }
test.py
import sys sys.path.append('.') import mymod print(mymod.add(1, 2)) print(mymod.add(1))
Makefile (на всякий случай):
all: mymod.so
mymod.so: mymod.c Makefile gcc -shared -fPIC -o mymod.so mymod.c `python3-config --includes --libs --cflags --ldflags`
clean: -rm mymod.so
Кроме прочего, меня смущает, что когда не передаешь второй аргумент, функция PyArg_ParseTuple все равно его поганит непонятно чем, хотя он изначально 0. Как с этим бороться?
Упс, PyArg_ParseTuple перестала поганить второй аргумент. Наверное, забыл сделать make перед запуском test.py. Но это не снимает основного вопроса.


Ответ

Для сравнения, вот C версия, реализующая Питон-функцию:
def add(*args, init=0): for s in args: init += s return init
которая пытается ссылки считать (чтобы память не утекала) и не игнорировать возможные ошибки (чтобы исключение получить, а не segfault):
static PyObject* mymod_add(PyObject *self, PyObject *args, PyObject *kwds) { // parse only keyword args PyObject *init = NULL; static char *kwlist[] = {"init", NULL};
PyObject *emptytuple = PyTuple_New(0); if (emptytuple == NULL) return NULL; int ret = PyArg_ParseTupleAndKeywords(emptytuple, kwds, "|O", kwlist, &init); Py_DECREF(emptytuple); if (!ret) return NULL;
if (init == NULL) { // default init = PyLong_FromLong(0); // init=0 if (init == NULL) return NULL; } else { Py_INCREF(init); // get ownership }
// sum args Py_ssize_t tuplesize = PyTuple_Size(args); Py_ssize_t i = 0; for ( ; i < tuplesize; ++i) { PyObject *item = PyTuple_GetItem(args, i); // borrowed ref if (item == NULL) { Py_DECREF(init); return NULL; }
//NOTE: assume tuple is immutable: no Py_INCREF(item)/Py_DECREF(item) PyObject *temp = PyNumber_InPlaceAdd(init, item); // new ref Py_DECREF(init); if (temp == NULL) return NULL; init = temp; } return init; }
static PyMethodDef module_functions[] = { {"add", (PyCFunction)mymod_add, METH_VARARGS | METH_KEYWORDS, "add arguments"}, {NULL, NULL, 0, NULL} };

Комментариев нет:

Отправить комментарий