Привет. Самообучаюсь написанию модулей на 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}
};
Комментариев нет:
Отправить комментарий