So übergeben Sie ein Array von C an ein eingebettetes Python-Skript

8

Ich laufe auf einige Probleme und möchte etwas Hilfe. Ich habe einen Stückcode, mit dem ein Python-Skript eingebettet wird. Dieses Python-Skript enthält eine Funktion, die erwartet, ein Array als Argument zu erhalten (in diesem Fall verwende ich numpy array innerhalb des Python-Skripts). Ich würde gerne wissen, wie ich ein Array von C an das eingebettete Python-Skript als Argument für die Funktion innerhalb des Skripts übergeben kann. Genauer gesagt kann mir jemand ein einfaches Beispiel dafür zeigen.

    
user1750948 18.12.2012, 21:55
quelle

2 Antworten

15

Die beste Antwort hier ist wahrscheinlich, dass Sie numpy -Arrays ausschließlich verwenden, sogar aus Ihrem C-Code. Aber wenn das nicht möglich ist, haben Sie das gleiche Problem wie jeder Code, der Daten zwischen C-Typen und Python-Typen teilt.

Im Allgemeinen gibt es mindestens fünf Optionen für die gemeinsame Nutzung von Daten zwischen C und Python:

  1. Erstelle ein Python list oder ein anderes passendes Objekt.
  2. Definieren Sie einen neuen Python-Typ (in Ihrem C-Code) zum Umbrechen und Darstellen des Arrays mit den gleichen Methoden, die Sie für ein Sequenzobjekt in Python ( __getitem__ , etc.) definieren würden.
  3. Wirf den Zeiger auf das Array auf intptr_t oder auf den expliziten ctypes -Typ oder lasse ihn einfach un-cast; Verwenden Sie dann ctypes auf der Python-Seite, um darauf zuzugreifen.
  4. Wirf den Zeiger auf das Array auf const char * und übergib es als str (oder in Py3, bytes ) und benutze struct oder ctypes auf der Python-Seite, um darauf zuzugreifen.
  5. Erstellen Sie ein Objekt, das dem Protokoll buffer entspricht, und verwenden Sie erneut struct oder ctypes auf der Python-Seite.

In Ihrem Fall möchten Sie numpy.array s in Python verwenden. So werden die allgemeinen Fälle:

  1. Erstellen Sie ein numpy.array , das übergeben werden soll.
  2. (wahrscheinlich nicht passend)
  3. Übergeben Sie den Zeiger an das Array, wie er ist, und verwenden Sie in Python ctypes , um es in einen Typ zu konvertieren, den numpy in ein Array konvertieren kann.
  4. Wirf den Zeiger auf das Array auf const char * und übergib es als str (oder in Py3, bytes ), was bereits ein Typ ist, den numpy in ein Array umwandeln kann.
  5. Erstellen Sie ein Objekt, das mit dem buffer -Protokoll übereinstimmt, und das wiederum, wie ich glaube, numpy direkt konvertieren kann.

Für 1, hier ist, wie man es mit einem list macht, nur weil es ein sehr einfaches Beispiel ist (und ich habe es bereits geschrieben ...):

%Vor%

Und hier ist die numpy.array Entsprechung (vorausgesetzt, Sie können sich darauf verlassen, dass die C array nicht gelöscht wird - siehe Erstellen von Arrays in den Dokumenten für weitere Details zu Ihren Optionen hier:

%Vor%

Wie auch immer Sie dies tun, Sie werden am Ende etwas haben, das wie ein PyObject * von C aussieht (und einen einzigen Refcount hat), so dass Sie es als Funktionsargument weitergeben können, während es auf der Python-Seite ist Es sieht aus wie ein numpy.array , list , bytes oder was auch immer angemessen ist.

Nun, wie übergeben Sie tatsächlich Funktionsargumente? Nun, der Beispielcode in Pure Embedding , auf den Sie in Ihrem Kommentar verwiesen haben, zeigt, wie Sie dies tun können. aber erklärt nicht wirklich, was vor sich geht. Es gibt tatsächlich mehr Erklärungen in den erweiterten Dokumenten als in den eingebetteten Dokumenten, insbesondere Python-Funktionen aufrufen von C . Beachten Sie auch, dass der Standardbibliotheksquellcode mit Beispielen dafür vollgestopft ist (obwohl einige von Sie sind nicht so lesbar, wie sie sein könnten, entweder wegen der Optimierung oder weil sie nicht aktualisiert wurden, um die Vorteile der neuen vereinfachten C-API-Funktionen zu nutzen.

Überspringen Sie das erste Beispiel zum Abrufen einer Python-Funktion aus Python, weil Sie das vermutlich bereits haben. Das zweite Beispiel (und der Absatz rechts darüber) zeigt die einfache Möglichkeit, dies zu tun: Erstellen eines Argumenttupels mit Py_BuildValue . Nehmen wir an, wir möchten eine Funktion aufrufen, die Sie in myfunc gespeichert haben, mit der Liste mylist , die von dieser makelist Funktion zurückgegeben wird. Hier ist, was Sie tun:

%Vor%

Sie können die aufrufbare Prüfung überspringen, wenn Sie sicher sind, dass Sie ein gültiges aufrufbares Objekt haben. (Und es ist normalerweise besser zu überprüfen, wann Sie zum ersten Mal myfunc erhalten, da Sie auf diese Weise sowohl frühere als auch bessere Fehlerrückmeldungen geben können.)

Wenn Sie wirklich verstehen wollen, was vor sich geht, versuchen Sie es ohne Py_BuildValue . Wie die Dokumente sagen, ist das zweite Argument für [PyObject_CallObject][6] ein Tupel und PyObject_CallObject(callable_object, args) entspricht apply(callable_object, args) , was callable_object(*args) entspricht. Also, wenn Sie myfunc(mylist) in Python aufrufen wollten, müssen Sie das in myfunc(*(mylist,)) umwandeln, damit Sie es in C übersetzen können. Sie können ein tuple wie folgt konstruieren:

%Vor%

Aber normalerweise ist Py_BuildValue einfacher (besonders, wenn Sie nicht schon alles als Python-Objekte gepackt haben) und die Absicht in Ihrem Code ist klarer (genauso wie PyArg_ParseTuple einfacher und klarer ist als explizite tuple funktioniert in der anderen Richtung).

Also, wie bekommst du das myfunc ? Nun, wenn Sie die Funktion aus dem Embedding-Code erstellt haben, halten Sie einfach den Zeiger herum. Wenn Sie möchten, dass es aus dem Python-Code übernommen wird, ist das genau das, was das erste Beispiel tut. Wenn Sie zB nach Namen aus einem Modul oder einem anderen Kontext suchen möchten, verwenden Sie die APIs für konkrete Typen wie %. co_de% und abstrakte Typen wie PyModule sind ziemlich einfach, und es ist allgemein offensichtlich, wie man Python-Code in den entsprechenden C-Code umwandelt, selbst wenn das Ergebnis meist hässlich ist.

Wenn wir das alles zusammensetzen, sagen wir, ich habe ein C-Array mit ganzen Zahlen, und ich möchte PyMapping und eine Funktion import mymodule aufrufen, die ein int zurückgibt. Hier ist ein abgespecktes Beispiel (nicht wirklich getestet und keine Fehlerbehandlung, aber es sollte alle Teile anzeigen):

%Vor%

Wenn Sie mit C ++ arbeiten, möchten Sie wahrscheinlich in eine Art von Schutz / Hausmeister / etc. um all diese mymodule.myfunc(mylist) -Aufrufe zu verarbeiten, vor allem, wenn Sie mit der richtigen Fehlerbehandlung beginnen (was normalerweise bedeutet, dass frühe Py_DECREF -Aufrufe durch die Funktion gespickt sind). Wenn Sie C ++ 11 oder Boost verwenden, ist return NULL möglicherweise alles, was Sie brauchen.

Aber wirklich, ein besserer Weg, um all das hässliche Boilerplate zu reduzieren, wenn Sie eine Menge C & lt; - & gt; Python-Kommunikation planen, ist es, alle vertrauten Frameworks zu betrachten, die zur Verbesserung von Python- Cython , boost :: python usw. Auch wenn Sie einbetten, führen Sie effektiv die gleiche Arbeit aus wie das Erweitern, damit sie auf die gleiche Weise helfen können.

Einige von ihnen auch haben Werkzeuge, um den Einbettungsteil zu unterstützen, wenn Sie in den Dokumenten suchen. Beispielsweise können Sie Ihr Hauptprogramm in Cython schreiben, indem Sie sowohl C-Code als auch Python-Code verwenden und unique_ptr<PyObject, Py_DecRef> . Vielleicht möchten Sie Ihre Finger drücken und / oder einige Hühner opfern, aber wenn es funktioniert, ist es erstaunlich einfach und produktiv. Boost ist nicht annähernd so trivial, aber wenn man alles zusammen hat, wird fast alles genau so ausgeführt, wie man es erwarten würde, und es funktioniert einfach, und das gilt auch für Einbetten als Erweiterung. Und so weiter.

    
abarnert 18.12.2012, 22:04
quelle
1

Für die Python-Funktion muss ein Python-Objekt übergeben werden. Da das Python-Objekt ein NumPy-Array sein soll, sollten Sie eines der NumPy C-API-Funktionen zum Erstellen von Arrays ; PyArray_SimpleNewFromData() ist wahrscheinlich ein guter Anfang. Es wird den bereitgestellten Puffer verwenden, ohne die Daten zu kopieren.

Das heißt, es ist fast immer einfacher, das Hauptprogramm in Python zu schreiben und ein C-Erweiterungsmodul für den C-Code zu verwenden. Dieser Ansatz macht es einfacher, Python die Speicherverwaltung zu überlassen, und das ctypes -Modul zusammen mit Numpys cpython -Erweiterungen macht es einfach, ein NumPy-Array an eine C-Funktion zu übergeben.

    
Sven Marnach 18.12.2012 22:12
quelle

Tags und Links