#python #c #python_c_api
Привет. Самообучаюсь написанию модулей на C для Python по этому адресу https://docs.python.org/3/c-api/arg.html (скоро по работе может пригодиться). Легко написал пример модуля с функцией add, которая суммирует два целых числа. Как получить количество аргументов, переданных в функцию, чтобы она все их просуммировала и вернула результат? Что и как изменить в коде? mymod.c: #includestatic 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. Но это не снимает основного вопроса.
Ответы
Ответ 1
Для сравнения, вот 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} };Ответ 2
Вариант с итератором, конечно, интереснее. Но приведу и свой код. #includestatic PyObject * mymod_add(PyObject *self, PyObject *args) { PyObject *item; int size; long sum = 0; size = PyTuple_Size(args); fprintf(stdout, "Arguments count: %d\n", size); for(int i=0; i < size; i++) { if(PyLong_Check(item = PyTuple_GetItem(args, i))) sum += PyLong_AsLong(item); else fprintf(stderr, "Failed arg № %d\n", i); } return Py_BuildValue("l", sum); } Ответ 3
Что типа этого надо: static PyObject * mymod_add(PyObject *self, PyObject *args) { double sum = 0; PyObject *iterator = PyObject_GetIter(args); if (iterator == NULL) return -1; while (true) { PyObject* value = PyIter_Next(iterator); if (value == NULL) break; sum += PyLong_AsDouble(value); Py_DECREF(value); } Py_DECREF(iterator); return PyLong_FromDouble(sum); }
Комментариев нет:
Отправить комментарий