2012-04-21 7 views
7

? Estoy empezando a jugar con las extensiones Python C y tengo curiosidad por saber por qué una función C, invocable desde Python debe tomar 2 argumentos PyObject * y devuelve un PyObject *. Escribí el siguiente "Hello World" extensión:Extensiones de Python C: ¿por qué las funciones C invocables toman argumentos y devuelven PyObject *

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

Por qué no puedo (especialmente con METH_NOARGS) utiliza el siguiente método hola_mundo:

static void 
hello_world() 
{ 
    printf("Hello World\n"); 
} 

?

Respuesta

10

Hay varias cosas que decir sobre los diversos punteros PyObject.

  1. la requerida como retorno tipo se utiliza para el mecanismo de manipulación de excepción. Específicamente, si su función devuelve un puntero nulo, el intérprete de Python lanzará una excepción. (Sólo se debe hacer eso después de llamar a uno de los PyErr_.. funciones para establecer una excepción específica.)

    Esto también significa que cada vez que no desea lanzar una excepción, debe devolver un puntero a algunos de los verdaderos PyObject. Si no se supone que su función vuelva a aparecer, simplemente devuelva Py_None (utilice mejor la macro Py_RETURN_NONE para obtener el recuento de referencias correcto) o "verdadero" (usando Py_RETURN_TRUE).

  2. El primer argumento, PyObject *self puntos en el objeto de la función se llama a partir de, o a la instancia de módulo al que pertenece. Tenga en cuenta que cada función que defina es un método de clase o un método de módulo. No hay funciones totalmente independientes.

  3. El segundo argumento, PyObject *args puntos al argumento de la función (que puede ser una tupla o lista de varios argumentos). Tiene razón al señalar que una función que no toma ningún argumento no debería necesitar este — y, por lo que yo sé, tiene razón.No tienes que definirlo; sólo tiene que definir una función que

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

    Usted todavía tendrá que jugar esta a PyCFunction cuando lo pones en el PyMethodDef para el tipo de datos que defina, pero yo creo que reparto es segura, siempre y cuando use la bandera METH_NOARGS. Pero tenga en cuenta los comentarios a continuación para posibles riesgos.

  4. Por último, una función puede de hecho tener un tercer argumento así:

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

    El tercer argumento se utiliza para llamado, opcionales argumentos. También en este caso, debe emitir el puntero de función a PyCFunction, pero eso también es seguro si establece la marca correcta (METH_KEYWORDS) para su función en la tabla de métodos.

+0

Es 'Py_None', no' PyNone'. También hay 'Py_RETURN_NONE' similar a' Py_RETURN_TRUE' y 'Py_RETURN_FALSE'. – yak

+0

Para (3), corrígeme si me equivoco pero si el código usa una convención de llamadas donde todos los argumentos se pasan en la pila y el llamado (la función) los saca de la pila, eliminar el argumento no utilizado provocaría un bloqueo porque Python siempre pasará NULL como el segundo arg y la función no lo abrirá. – yak

+0

@yak No causa un bloqueo en mi código. – jogojapan

2

Porque las funciones de Python, incluso las más triviales que simplemente imprimen en stdout, son más que simples envoltorios de funciones C.

En el caso más simple, piense en las instalaciones de introspección en Python. Una función de Python es un objeto-nido completo que se puede consultar:

>>> 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'] 

Se podría, por supuesto, imaginar un centro de extensión que acaba de terminar las funciones C. Consulte SWIG, que le permite escribir extensiones para muchos lenguajes de scripting, incluido Python. Debido a que va por el denominador común más bajo, te permitirá ajustar una función como tu hello_world, pero por supuesto también perderás mucha potencia.

4

El primer argumento para las funciones de nivel de módulo es el objeto del módulo. Al definir clases en C (la misma estructura PyMethodDef se utiliza para los métodos allí), el primer argumento es la instancia (similar a self en Python).

Al usar METH_NOARGS, Python pasará NULL como el segundo argumento. Podrían lanzarlo a una función con un argumento, pero supongo que no pensaron que fuera necesario.

El valor de retorno es fácil de explicar. Cada función de Python tiene un valor de retorno. Si no utiliza explícitamente return en Python, la función devolverá None.

Por supuesto, en C tiene que ser explícito sobre el valor de retorno, por lo que si no lo usa, tiene que devolver None usted mismo. Python proporciona una macro para esto:

Py_RETURN_NONE; 

alternativa usted puede acceder a la instancia global None mismo:

Py_INCREF(Py_None); 
return Py_None; 

pero la macro es más fácil de usar.

Podría pensar que devolver NULL debería ser equivalente a None, pero NULL se usa para indicar que la función generó una excepción.

Cuestiones relacionadas