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 указатели.

  1. требуется как тип возвращаемого используется механизм обработки исключений. в частности, если ваша функция возвращает нулевой указатель, то интерпретатор Python выдаст исключение. (Вы должны сделать это только после вызова одного из PyErr_.. функции для установки определенного исключения.)

    это также означает, что всякий раз, когда вы не хотите выбросить исключение, вы должен вернуть указатель на какой-то реальный PyObject. если нет ничего, в частности, ваша функция должна возвратить Py_None (лучше использовать Py_RETURN_NONE макрос, чтобы получить правильный счетчик ссылок), или " true "(используя Py_RETURN_TRUE).

  2. на первый аргумент, PyObject *self указывает на объект, из которого вызывается функция, или на экземпляр модуля, к которому она принадлежит. Обратите внимание, что все функции вы define - это либо метод класса, либо метод модуля. нет полностью независимых функций.

  3. на второй, PyObject *args указывает на аргумент функции (который может быть кортежем или списком из нескольких аргументов). Вы правы, указывая на то, что функция, которая не принимает никаких аргументов не нужно - и, насколько я могу судить, Вы правы. Вы не должны определить его, вы можете просто определить функцию as

    static PyObject *PyMyClass_MyFunc(PyObject *self) {
      /* ..do something.. */
      Py_RETURN_TRUE;
    }
    

    вам все равно придется бросить это PyCFunction когда вы кладете его в PyMethodDef для типа данных, который вы определяете, но я считаю, что приведение безопасно, пока вы используете METH_NOARGS флаг. но обратите внимание на комментарии ниже о возможных рисках.

  4. и, наконец, функция может быть третий аргумент такой:

    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, но, конечно, вы тоже теряете много энергии.