Страницы

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

суббота, 14 декабря 2019 г.

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

#python #c #python_c_api


Привет. Самообучаюсь написанию модулей на 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. Но это не снимает основного вопроса.
    


Ответы

Ответ 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



Вариант с итератором, конечно, интереснее. Но приведу и свой код. #include static 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); }

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

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