Python C Extensions-почему вызываемые функции C должны принимать аргументы и возвращать PyObject *
Я только начинаю играть с расширениями Python C и мне любопытно, почему функция C, которая вызывается из Python, должна принимать 2 аргумента PyObject* и возвращать PyObject*. Я написал следующее расширение "Hello World":
#include <Python.h>
static PyObject *
hello_world(PyObject *self, PyObject *noargs)
{
printf("Hello Worldn");
return Py_BuildValue("");
}
// Module functions table.
static PyMethodDef
module_functions[] = {
{ "hello_world", hello_world, METH_NOARGS, "hello world method" },
{ NULL }
};
// This function is called to initialize the module.
PyMODINIT_FUNC
inittesty2(void)
{
Py_InitModule("testy2", module_functions);
}
почему я не могу (особенно с METH_NOARGS) использовать следующий метод hello_world:
static void
hello_world()
{
printf("Hello Worldn");
}
?
3 ответов
есть несколько вещей, чтобы сказать о различных PyObject
указатели.
-
требуется как тип возвращаемого используется механизм обработки исключений. в частности, если ваша функция возвращает нулевой указатель, то интерпретатор Python выдаст исключение. (Вы должны сделать это только после вызова одного из
PyErr_..
функции для установки определенного исключения.)это также означает, что всякий раз, когда вы не хотите выбросить исключение, вы должен вернуть указатель на какой-то реальный
PyObject
. если нет ничего, в частности, ваша функция должна возвратитьPy_None
(лучше использоватьPy_RETURN_NONE
макрос, чтобы получить правильный счетчик ссылок), или " true "(используяPy_RETURN_TRUE
). на первый аргумент,
PyObject *self
указывает на объект, из которого вызывается функция, или на экземпляр модуля, к которому она принадлежит. Обратите внимание, что все функции вы define - это либо метод класса, либо метод модуля. нет полностью независимых функций.-
на второй,
PyObject *args
указывает на аргумент функции (который может быть кортежем или списком из нескольких аргументов). Вы правы, указывая на то, что функция, которая не принимает никаких аргументов не нужно - и, насколько я могу судить, Вы правы. Вы не должны определить его, вы можете просто определить функцию asstatic PyObject *PyMyClass_MyFunc(PyObject *self) { /* ..do something.. */ Py_RETURN_TRUE; }
вам все равно придется бросить это
PyCFunction
когда вы кладете его вPyMethodDef
для типа данных, который вы определяете, но я считаю, что приведение безопасно, пока вы используетеMETH_NOARGS
флаг. но обратите внимание на комментарии ниже о возможных рисках. -
и, наконец, функция может быть третий аргумент такой:
static PyObject *PyMyClass_Func(PyObject *self, PyObject *args, PyObject *kwds) { /*...*/ }
третий аргумент используется для имени, необязательно аргументов. В этом случае вы тоже должны наведите указатель функции на
PyCFunction
, но это тоже безопасно, если вы установите правильный флаг (METH_KEYWORDS
) для функции в таблице методов.
первым аргументом для функций уровня модуля является объект module. При определении классов в C (то же PyMethodDef
структура используется для методов там), первым аргументом является экземпляр (аналогично self
в Python).
при использовании METH_NOARGS
, Python пройдет NULL
как второй аргумент. Они могли бы привести его к функции с одним аргументом, но я думаю, они не думали, что это необходимо.
возвращаемое значение легко объяснить. Каждая функция Python имеет значение. Если вы явно не используете return
в Python функция будет возвращать None
.
конечно, в C вы должны быть явными о возвращаемом значении, поэтому, если вы не используете его, вы должны вернуть None
себя. Python предоставляет макрос для этого:
Py_RETURN_NONE;
кроме того, вы можете получить доступ к глобальной None
экземпляр себе:
Py_INCREF(Py_None);
return Py_None;
но макрос проще в использовании.
можно подумать, что вернувшись NULL
должно быть эквивалентно None
но NULL
используется для указания на то, что функция вызвала исключение.
потому что функции Python, даже тривиальные, которые просто печатаются в stdout, - это больше, чем просто обертки вокруг функций C.
в простейшем случае подумайте о средствах интроспекции в Python. Функция Python-это объект full-fledge, который вы можете запросить:
>>> def hello():
... print 'hello'
...
>>> dir(hello)
['__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__doc__', '__format__', '__get__', '__getattribute__', '__globals__', '__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name']
вы могли бы, конечно, представить себе средство расширения, которое только что обернуло функции C.
Проверьте глоток, что позволяет создавать расширения для множества языков сценариев, включая Питон. Поскольку он идет на самый низкий общий знаменатель, он позволит вам обернуть функцию, такую как ваш hello_world
, но, конечно, вы тоже теряете много энергии.