Rufen Sie den Python-Code von c über cython auf

Lesezeit: 6 Minuten

Benutzer-Avatar
Pius

Ich möchte also einen Python-Code von c über cython aufrufen. Ich habe es geschafft, Cython-Code von c aufzurufen. Und ich kann auch Python-Code von Cython aufrufen. Aber wenn ich alles zusammenzähle, fehlen einige Dinge.

Hier ist mein Python-Code (quacker.pyx):

def quack():
    print "Quack!"

Hier ist meine Cython “Brücke” (caller.pyx):

from quacker import quack

cdef public void call_quack():
    quack()

Und hier ist der C-Code (main.c):

#include <Python.h>
#include "caller.h"

int main() {
  Py_Initialize();
  initcaller();
  call_quack();
  Py_Finalize();
  return 0;
}

Wenn ich das ausführe, bekomme ich diese Ausnahme:

Exception NameError: "name 'quack' is not defined" in 'caller.call_quack' ignored

Die fehlenden Teile, die ich vermute:

  • Ich habe nicht angerufen initquacker()
  • Ich habe nicht eingeschlossen quacker.h
  • Cython hat keine produziert quacker.honly quacker.c
  • caller.c importiert nicht quacker.h oder anrufen initquacker()

Ich bin mir nicht wirklich sicher, ob es überhaupt möglich ist, das zu tun, was ich versuche, aber es scheint mir, dass es so sein sollte. Ich würde gerne jede Eingabe hören, die Sie haben könnten.

Bearbeiten:

So cythonisiere / kompiliere / verlinke / führe ich aus:

$ cython *.pyx
$ cc -c *.c -I/System/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7
$ cc -L/System/Library/Frameworks/Python.framework/Versions/2.7/lib -L/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/config -lpython2.7 -ldl *.o -o main
$ ./main

  • Kann die Quacker-Datei einfach eine Python-.py-Datei sein, oder muss es eine .pyx-Datei sein?

    – brm

    23. März 2014 um 13:29 Uhr

  • Kannst du posten, wie du das alles zusammengestellt hast?

    – hivert

    23. März 2014 um 14:36 ​​Uhr

  • @brm Ja, das würde ich sogar bevorzugen, da sich der gesamte Python-Code, auf den ich zugreifen möchte, bereits in .py-Dateien befindet.

    – Pius

    23. März 2014 um 15:51 Uhr

  • @hivert Ich habe mein hausgemachtes Build-Skript in einer Bearbeitung hinzugefügt

    – Pius

    23. März 2014 um 15:52 Uhr


Benutzer-Avatar
brm

Wenn Sie die umbenennen quacker.pyx zu quacker.py, ist eigentlich alles richtig. Das einzige Problem ist, dass Ihr Programm im aktuellen Verzeichnis nicht nach Python-Modulen sucht, was zu folgender Ausgabe führt:

Exception NameError: "name 'quack' is not defined" in 'caller.call_quack' ignored

Wenn Sie jedoch das aktuelle Verzeichnis zur Umgebungsvariablen PYTHONPATH hinzufügen, wird die Ausgabe wie erwartet aussehen:

$ PYTHONPATH=".:$PYTHONPATH" ./main 
Quack!

Beim Ausführen der Python-Shell gemäß der Dokumentation das aktuelle Verzeichnis (oder das Verzeichnis, das das Skript enthält) wird hinzugefügt sys.path Variable automatisch, aber beim Erstellen eines einfachen Programms mit Py_Initialize und Py_Finalize das scheint nicht zu passieren. Da die Variable PYTHONPATH auch zum Auffüllen der sys.path python-Variable erzeugt die obige Problemumgehung das richtige Ergebnis.

Alternativ unterhalb der Py_Intialize Zeile, könnten Sie eine leere Zeichenfolge hinzufügen sys.path wie folgt, indem Sie einfach einen Python-Code ausführen, der als Zeichenfolge angegeben ist:

PyRun_SimpleString("import sys\nsys.path.insert(0,'')");

Nach dem Neukompilieren läuft es einfach ./main sollte dann funktionieren.

Bearbeiten

Es ist tatsächlich interessant zu sehen, was passiert, wenn Sie den Code wie in der Frage angegeben ausführen, also ohne Umbenennung der quacker.pyx Datei. In diesem Fall ist die initcaller() Funktion versucht, die zu importieren quacker Modul, aber da nein quacker.py oder quacker.pyc vorhanden ist, das Modul nicht gefunden werden kann und die initcaller() Funktion erzeugt einen Fehler.

Jetzt wird dieser Fehler auf Python-Weise gemeldet, indem eine Ausnahme ausgelöst wird. Aber der Code in der main.c Datei prüft dies nicht. Ich bin kein Experte darin, aber in meinen Tests füge ich den folgenden Code hinzu initcaller() schien zu funktionieren:

if (PyErr_Occurred())
{
    PyErr_Print();
    return -1;
}

Die Ausgabe des Programms sieht dann wie folgt aus:

Traceback (most recent call last):
  File "caller.pyx", line 1, in init caller (caller.c:836)
    from quacker import quack
ImportError: No module named quacker

Durch den Aufruf der initquacker() Funktion Vor initcaller()der Modulname quacker schon wird also der importaufruf eingetragen, das ist drinnen erledigt initcaller() erkennt, dass es bereits geladen ist, und der Aufruf wird erfolgreich sein.

  • Alternativ können Sie die einstellen PYTHONPATH mit PySys_SetPath("..."); wo … ist das, was Sie wollen.

    – hivert

    23. März 2014 um 17:07 Uhr


  • @hivert Wenn Sie das nur tun würden, würde sys.path nur auf ‘.’ gesetzt, also vermute ich, dass es schwierig wäre, andere Standardmodule zu finden

    – brm

    23. März 2014 um 17:10 Uhr

  • Gute Antwort :-). Ich denke, ich ziehe es vor, die zu setzen sys.path.insert Rufen Sie oben an caller.pyx. Und das PyErr_Occurred Trick funktioniert super.

    – Pius

    23. März 2014 um 20:19 Uhr

Falls sich jemand fragt, wie es in Python 3 funktionieren würde, hier ist meine Lösung, nachdem ich als Cython-Neuling ein wenig gekämpft habe.

Haupt c

#include <Python.h>
#include "caller.h"

int
main() 
{
    PyImport_AppendInittab("caller", PyInit_caller);
    Py_Initialize();
    PyImport_ImportModule("caller");
    call_quack();
    Py_Finalize();
    return 0;
}

Anrufer.pyx

# cython: language_level=3
import sys
sys.path.insert(0, '')

from quacker import quack

cdef public void call_quack():
    quack()

quacker.py

def quack():
    print("Quack!")

Endlich, hier ist die Makefile das kompiliert alles:

target=main
cybridge=caller

CC=gcc
CFLAGS= `python3-config --cflags`
LDFLAGS=`python3-config --ldflags`

all:
        cython $(cybridge).pyx
        $(CC) $(CFLAGS) -c *.c
        $(CC) $(LDFLAGS) *.o -o $(target)

clean:
        rm -f $(cybridge).{c,h,o} $(target).o $(target)
        rm -rf __pycache__

Vielleicht ist das nicht das, was Sie wollen, aber ich habe es durch die folgenden Änderungen zum Laufen gebracht:

in quacker.pyx habe ich hinzugefügt

cdef public int i

Um Cython zu zwingen, die .h Datei.

Und dann in der Hauptsache:

#include <Python.h>
#include "caller.h"
#include "quacker.h"

int main() {
  Py_Initialize();
  initquacker();
  initcaller();
  call_quack();
  Py_Finalize();
  return 0;
}

  • Schön, das geht :-). Es ist allerdings etwas hackig. Um diese Methode zu verwenden, muss ich falsche Attribute in alle meine Python-Dateien einfügen. Es bedeutet auch, dass mein C-Code alle meine Python-Module kennen muss. Ich würde es vorziehen, dass der C-Code nur die Bridge (caller.pyx) kennt.

    – Pius

    23. März 2014 um 16:06 Uhr

  • Sie brauchen diese Header-Datei eigentlich nicht. In C können Funktionen implizit deklariert werden, sodass der Code auch ohne Header-Datei funktioniert, indem man einfach initquacker() aufruft

    – brm

    23. März 2014 um 16:38 Uhr

  • Das stimmt! Ich habe eigentlich nur aufgegeben, nachdem der Compiler mir diese Warnung gegeben hat: main.c:7:3: warning: implicit declaration of function 'initquacker' is invalid in C99 [-Wimplicit-function-declaration]

    – Pius

    23. März 2014 um 20:02 Uhr

Ich musste dies mit CMake tun und habe dieses Beispiel schließlich neu erstellt. Sie finden das Repository mit einem vollständigen Arbeitsbeispiel hier.

Sie können das Beispiel entweder mit Docker auf der CLI oder mit Visual Studio erstellen und ausführen Entwicklungscontainer.

1334550cookie-checkRufen Sie den Python-Code von c über cython auf

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy