2012-04-21 6 views
7

Ich fange gerade an, mit Python C Erweiterungen zu spielen und bin gespannt, warum eine C-Funktion, die von Python aufgerufen werden kann, 2 PyObject * Argumente benötigt und gib ein PyObject * zurück. Ich schrieb die folgende "Hallo Welt" Erweiterung:Python C Extensions - Warum müssen aufrufbare C-Funktionen Argumente annehmen und PyObject zurückgeben *

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

Warum kann ich nicht (vor allem mit METH_NOARGS) verwenden Sie die folgende hello_world Methode:

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

?

Antwort

10

Es gibt mehrere Dinge zu den verschiedenen PyObject Zeigern zu sagen.

  1. Die eine erforderliche als Rückgabetyp wird für die Ausnahmebehandlungsmechanismus verwendet. Wenn Ihre Funktion einen Nullzeiger zurückgibt, löst der Python-Interpreter eine Ausnahme aus. (Sie sollten nur tun, dass nach einer der PyErr_.. Aufrufen von Funktionen eine spezifische Ausnahme einzustellen.)

    Dies bedeutet auch, dass, wenn Sie nicht wollen, eine Ausnahme zu werfen, Sie einen Zeiger auf einige echte PyObject zurückkehren. Wenn Ihre Funktion nicht zurückgegeben werden soll, geben Sie einfach Py_None zurück (verwenden Sie am besten das Makro Py_RETURN_NONE, um den Referenzzähler richtig zu berechnen) oder "true" (mit Py_RETURN_TRUE).

  2. Das erste Argument, PyObject *self Punkte auf dem Objekt die Funktion aus, oder an die Modulinstanz aufgerufen wird, zu der es gehört. Beachten Sie, dass jede von Ihnen definierte Funktion entweder eine Klassenmethode oder eine Modulmethode ist. Es gibt keine völlig unabhängigen Funktionen.

  3. Der zweite Argument PyObject *args zeigt auf das Funktionsargument (die ein Tupel oder eine Liste von mehreren Argumenten sein kann). Sie haben recht, als Sie darauf hingewiesen haben, dass eine Funktion, die keine Argumente benötigt, diese — nicht benötigt, und soweit ich das beurteilen kann, haben Sie Recht.Sie müssen es nicht definieren; Sie können einfach eine Funktion als

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

    Sie werden immer noch zu Besetzung dieser zu PyCFunction haben definieren, wenn Sie es in die PyMethodDef stellen für den Datentyp Sie definieren, aber ich glaube, dass Guss solange man sicher ist, Verwenden Sie die METH_NOARGS Flagge. Beachten Sie jedoch die folgenden Kommentare für mögliche Risiken.

  4. Schließlich wird eine Funktion in der Tat ein drittes Argument wie dies kann:

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

    Das dritte Argument ist für verwendet genannt, optional Argumente. In diesem Fall müssen Sie auch den Funktionszeiger auf PyCFunction umwandeln, aber das ist auch sicher, wenn Sie das richtige Flag (METH_KEYWORDS) für Ihre Funktion in der Methodentabelle festlegen.

+0

Es ist 'Py_None', nicht' PyNone'. Außerdem gibt es 'Py_RETURN_NONE', ähnlich wie' Py_RETURN_TRUE' und 'Py_RETURN_FALSE'. – yak

+0

Für (3), korrigieren Sie mich, wenn ich falsch liege, aber wenn der Code eine Aufrufkonvention verwendet, bei der alle Argumente auf dem Stapel übergeben werden und der Aufrufer (die Funktion) sie aus dem Stapel löscht, würde das Entfernen des unbenutzten Arguments a verursachen abstürzen, weil Python als zweites Argument immer NULL übergibt und die Funktion es nicht aufgibt. – yak

+0

@yak Es verursacht keinen Absturz in meinem Code. – jogojapan

2

Da Python-Funktionen, sogar triviale, die nur auf stdout drucken, sind mehr als nur Wrapper um C-Funktionen.

Denken Sie im einfachsten Fall an die Introspektionsfunktionen in Python. Eine Python-Funktion ist ein voll flügge Objekt, das Sie abfragen:

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

Sie könnte natürlich eine Erweiterung Anlage vorstellen, die nur C-Funktionen gewickelt. Überprüfen Sie SWIG, die Ihre Erweiterungen für viele Skriptsprachen schreiben, einschließlich Python. Weil es für den kleinsten gemeinsamen Nenner geht, lässt es dich eine Funktion wie deine hello_world wickeln, aber natürlich verlierst du auch viel Energie.

4

Das erste Argument für Funktionen auf Modulebene ist das Modulobjekt. Wenn Klassen in C definiert werden (dieselbe PyMethodDef Struktur wird dort für Methoden verwendet), ist das erste Argument die Instanz (ähnlich wie self in Python).

Wenn Sie METH_NOARGS verwenden, wird Python NULL als zweites Argument übergeben. Sie könnten es mit einem Argument auf eine Funktion anwenden, aber ich denke, sie haben es nicht für nötig gehalten.

Der Rückgabewert ist leicht zu erklären. Jede Python-Funktion hat einen Rückgabewert. Wenn Sie in Python return nicht explizit verwenden, gibt die Funktion None zurück.

Natürlich müssen Sie in C explizit über den Rückgabewert sein, also wenn Sie es nicht verwenden, müssen Sie None selbst zurückgeben. Python bietet einen Makro dafür:

Py_INCREF(Py_None); 
return Py_None; 

aber das Makro ist einfacher zu bedienen:

Py_RETURN_NONE; 

Alternativ Sie die globale None Instanz selbst zugreifen.

Sie könnten denken, dass die Rückgabe von NULLNone entspricht, aber NULL wird verwendet, um anzuzeigen, dass die Funktion eine Ausnahme ausgelöst hat.