Python C Extensions - 为什么必须可调用的C函数接受参数并返回PyObject *

时间:2012-04-21 04:50:15

标签: python c python-c-extension

我刚刚开始使用Python C扩展,并且很好奇为什么可以从Python调用的C函数必须使用2个PyObject *参数并返回PyObject *。我写了以下“Hello World”扩展名:

#include <Python.h>

static PyObject *
hello_world(PyObject *self, PyObject *noargs)
{
   printf("Hello World\n");
   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 World\n");
}

3 个答案:

答案 0 :(得分:10)

关于各种PyObject指针,有几点要说。

  1. 返回类型所需的那个用于异常处理机制。具体来说,如果你的函数返回一个空指针,Python解释器将抛出一个例外。 (只有在调用其中一个PyErr_..函数来设置特定异常后才应该这样做。)

    这也意味着每当你不想抛出异常时,必须返回一个指向真实PyObject的指针。如果没有特别的东西你的函数应该返回,只需返回Py_None(最好使用Py_RETURN_NONE宏来使引用计数正确)或“true”(使用Py_RETURN_TRUE)。

  2. 第一个参数PyObject *self指向调用函数的对象,或指向它所属的模块实例。请注意,您定义的每个函数都是类方法或模块方法。没有完全独立的函数。

  3. 第二个参数,PyObject *args指向函数参数(可以是元组或多个参数的列表)。你是正确的指出一个不带任何参数的函数不应该需要这个 - 而且,据我所知,你是对的。你不必定义它;你可以简单地将函数定义为

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

    当你将{strong}加入到你定义的数据类型的PyCFunction时,你仍然需要强制转换为PyMethodDef ,但我相信强制转换是安全的当你使用METH_NOARGS标志时。 但请注意以下评论可能存在的风险。

  4. 最后,一个函数实际上可能有一个第三个参数,如下所示:

    static PyObject *PyMyClass_Func(PyObject *self, PyObject *args, PyObject *kwds)
    {
      /*...*/
    }
    

    第三个参数用于命名的,可选的参数。在这种情况下,您也必须将函数指针强制转换为PyCFunction ,但如果您为函数设置了正确的标志(METH_KEYWORDS),这也是安全的。方法表。

答案 1 :(得分:4)

模块级函数的第一个参数是模块对象。在C中定义类时(相同的PyMethodDef结构用于那里的方法),第一个参数是实例(类似于Python中的self)。

使用METH_NOARGS时,Python会将NULL作为第二个参数传递。他们可以使用一个参数将其转换为函数,但我猜他们认为不需要它。

返回值很容易解释。每个Python函数都有一个返回值。如果您未在Python中明确使用return,则该函数将返回None

当然在C语言中你必须明确返回值,所以如果你不使用它,你必须自己返回None。 Python为此提供了一个宏:

Py_RETURN_NONE;

或者,您可以自己访问全局None实例:

Py_INCREF(Py_None);
return Py_None;

但宏更容易使用。

您可以认为返回NULL应该等同于None,但NULL用于表示该函数引发了异常。

答案 2 :(得分:2)

因为Python函数,即使只是打印到stdout的微不足道的函数,也不仅仅是围绕C函数的包装器。

在最简单的情况下,考虑Python中的内省设施。 Python函数是一个完整的对象,您可以查询:

>>> 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函数的扩展工具。 查看SWIG,它允许您编写许多脚本语言的扩展,包括Python。因为它是最低的共同点,所以它会让你像hello_world一样包裹一个函数,但当然你也失去了很多力量。