7

我刚刚开始使用 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");
}

?

4

3 回答 3

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;
    }
    

    当您将其放入您定义的数据类型时,您仍然必须将其转换PyCFunctionPyMethodDef为,但我相信只要您使用该METH_NOARGS标志,转换是安全的。但请注意以下评论以了解可能存在的风险。

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

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

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

于 2012-04-21T10:04:28.587 回答
4

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

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

返回值很容易解释。每个 Python 函数都有一个返回值。如果您没有return在 Python 中显式使用,该函数将返回None.

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

Py_RETURN_NONE;

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

Py_INCREF(Py_None);
return Py_None;

但宏更容易使用。

你可以认为 returnNULL应该等价于NonebutNULL用于表示函数引发了异常。

于 2012-04-21T10:07:22.513 回答
2

因为 Python 函数,即使只是打印到标准输出的微不足道的函数,也不仅仅是 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,但当然你也失去了很多权力。

于 2012-04-21T05:18:46.273 回答