Erstellen einer C++-Erweiterung für Python in Visual Studio
In diesem Artikel erstellen Sie ein C++-Erweiterungsmodul für CPython, um einen hyperbolischen Tangens zu berechnen und ihn aus Python-Code aufzurufen. Die Routine wird zuerst in Python implementiert, um den relativen Leistungsgewinn durch Implementieren derselben Routine in C++ zu veranschaulichen.
In C++ (oder C) geschriebene Codemodule werden häufig verwendet, um die Fähigkeiten eines Python-Interpreters zu erweitern. Es gibt drei Haupttypen von Erweiterungsmodulen:
- Beschleunigermodule: Ermöglichen die beschleunigte Leistung. Da Python eine interpretierte Sprache ist, können Sie ein Beschleunigermodul in C++ erstellen, um eine höhere Leistung zu erzielen.
- Wrapper-Module: machen bestehende C/C++-Schnittstellen für den Python-Code verfügbar oder stellen eine „Python-ähnlichere“ API zur Verfügung, die mit Python einfach zu verwenden ist.
- Systemzugriffsmodule auf niedriger Ebene: Erstellen Sie Systemzugriffsmodule, um Features der
CPython
-Runtime auf niedriger Ebene, des Betriebssystems oder der zugrunde liegenden Hardware zu erreichen.
Dieser Artikel veranschaulicht zwei Möglichkeiten, um ein C++-Erweiterungsmodul in Python zur Verfügung zu stellen:
- Verwendung der
CPython
-Standarderweiterungen, die in der Python-Dokumentation beschrieben werden. - Verwendung von PyBind11, das aufgrund der Einfachheit für C++11 empfohlen wird. Um die Kompatibilität sicherzustellen, achten Sie darauf, dass Sie mit einer der neuesten Versionen von Python arbeiten.
Das fertige Beispiel aus dieser exemplarischen Vorgehensweise finden Sie auf GitHub unter python-samples-vs-cpp-extension.
Voraussetzungen
Eine Installation von Visual Studio 2017 oder höher mit der Workload für die Python-Entwicklung. Die Workload umfasst die nativen Python-Entwicklungstools, die die C++-Workload und Toolsets hinzufügen, die für native Erweiterungen erforderlich sind.
Weitere Informationen zu Installationsoptionen finden Sie unter Installieren der Python-Unterstützung für Visual Studio.
Hinweis
Bei Installation der Workload Data Science und analytische Anwendungen werden standardmäßig auch Python und die Option Native Python-Entwicklungstools installiert.
Wenn Sie Python separat installieren, stellen Sie sicher, dass Sie im Python-Installer unter Erweiterte Optionen die Optionen Debugging-Symbole herunterladen auswählen. Diese Option ist erforderlich, um die Debugfunktion im gemischten Modus zwischen Python-Code und nativem Code verwenden zu können.
Erstellen der Python-Anwendung
Befolgen Sie diese Schritte, um die Python-Anwendung zu erstellen.
Erstellen Sie ein neues Python-Projekt in Visual Studio, indem Sie Datei>Neu>Projekt auswählen.
Suchen Sie im Dialogfeld Neues Projekt erstellen nach python. Wählen Sie die Vorlage Python-Anwendung und anschließend Weiter aus.
Geben Sie einen Projektnamen und einen Speicherort ein, und wählen Sie dann Erstellen aus.
Visual Studio erstellt daraufhin das neue Projekt. Das Projekt wird im Projektmappen-Explorer geöffnet, und die Projektdatei (.py) wird im Code-Editor geöffnet.
Fügen Sie in die Datei .py den folgenden Code ein. Sie sollten den Code manuell eingeben, um einige Bearbeitungsfunktionen von Python kennenzulernen.
Dieser Code berechnet einen hyperbolischen Tangens, ohne die mathematische Bibliothek zu verwenden, und Sie können ihn später mit nativen Python-Erweiterungen beschleunigen.
Tipp
Schreiben Sie den Code in reinem Python, bevor Sie ihn in C++ neu schreiben. Auf diese Weise können Sie leichter überprüfen, ob der native Python-Code korrekt ist.
from random import random from time import perf_counter # Change the value of COUNT according to the speed of your computer. # The value should enable the benchmark to complete in approximately 2 seconds. COUNT = 500000 DATA = [(random() - 0.5) * 3 for _ in range(COUNT)] e = 2.7182818284590452353602874713527 def sinh(x): return (1 - (e ** (-2 * x))) / (2 * (e ** -x)) def cosh(x): return (1 + (e ** (-2 * x))) / (2 * (e ** -x)) def tanh(x): tanh_x = sinh(x) / cosh(x) return tanh_x def test(fn, name): start = perf_counter() result = fn(DATA) duration = perf_counter() - start print('{} took {:.3f} seconds\n\n'.format(name, duration)) for d in result: assert -1 <= d <= 1, " incorrect values" if __name__ == "__main__": print('Running benchmarks with COUNT = {}'.format(COUNT)) test(lambda d: [tanh(x) for x in d], '[tanh(x) for x in d] (Python implementation)')
Führen Sie das Programm aus, indem Sie Debuggen>Starten ohne Debuggen ausführen oder STRG+F5 drücken.
Ein Befehlsfenster wird geöffnet, um die Programmausgabe anzuzeigen.
Beachten Sie in der Ausgabe die für den Benchmark-Prozess angegebene Zeit.
Für diese exemplarische Vorgehensweise sollte der Benchmarkprozess ungefähr 2 Sekunden dauern.
Passen Sie bei Bedarf den Wert der
COUNT
-Variable im Code an, damit der Benchmark in etwa 2 Sekunden auf Ihrem Computer abgeschlossen werden kann.Führen Sie das Programm erneut aus, und bestätigen Sie, dass der geänderte
COUNT
-Wert den Benchmark in etwa 2 Sekunden erzeugt.
Tipp
Wenn Sie Benchmarks ausführen, verwenden Sie immer die Option Debuggen>Starten ohne Debuggen. Diese Methode trägt dazu bei, den Mehraufwand zu vermeiden, der entstehen kann, wenn Sie den Code im Visual Studio-Debugger ausführen.
Erstellen der C++-Hauptprojekte
Führen Sie die folgenden Schritte aus, um zwei identische C++-Projekte zu erstellen: superfastcode und superfastcode2. Später verwenden Sie in jedem Projekt einen anderen Ansatz, um den C++-Code für Python verfügbar zu machen.
Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf den Projektmappennamen, und wählen Sie Hinzufügen>Neues Projekt aus.
Eine Visual Studio-Projektmappe kann sowohl Python- als auch C++-Projekte enthalten (was einer der Vorteile von Visual Studio für die Python-Entwicklung ist).
Legen Sie im Dialogfenster Neues Projekt hinzufügen den Filter Sprache auf C++ fest, und geben Sie Leer im Suchfeld ein.
Wählen Sie in der Liste der Projektvorlagenergebnisse Leeres Projekt und dann Weiter aus.
Geben Sie im Dialogfeld Neues Projekt konfigurieren den Projektnamen ein:
- Geben Sie für das erste Projekt den Namen superfastcode ein.
- Geben Sie für das zweite Projekt den Namen superfastcode2 ein.
Klicken Sie auf Erstellen.
Wiederholen Sie diese Schritte, und erstellen Sie zwei Projekte.
Tipp
Ein alternativer Ansatz ist verfügbar, wenn Sie die nativen Python-Entwicklungstools in Visual Studio installiert haben. Sie können mit der Vorlage Python-Erweiterungsmodul beginnen, wodurch viele der in diesem Artikel beschriebenen Schritte bereits abgeschlossen sind.
Für die exemplarische Vorgehensweise in diesem Artikel hilft es, mit einem leeren Projekt zu beginnen, um Schritt für Schritt zu demonstrieren, wie das Erweiterungsmodul erstellt wird. Nachdem Sie den Prozess verstanden haben, können Sie die alternative Vorlage verwenden, um Zeit zu sparen, wenn Sie eigene Erweiterungen schreiben.
Hinzufügen einer C++-Datei zum Projekt
Fügen Sie als Nächstes jedem Projekt eine C++-Datei hinzu.
Erweitern Sie im Projektmappen-Explorer das Projekt, klicken Sie mit der rechten Maustaste auf den Knoten Quelldateien, und wählen Sie Hinzufügen>Neues Element aus.
Wählen Sie in der Liste der Dateivorlagen die C++-Datei (.cpp) aus.
Geben Sie den Namen für die Datei als module.cpp ein, und wählen Sie dann Hinzufügen aus.
Wichtig
Stellen Sie sicher, dass der Dateiname die .cpp-Erweiterung enthält. Visual Studio sucht nach einer Datei mit der .cpp-Erweiterung, um die Anzeige der C++-Projekteigenschaftenseiten zu ermöglichen.
Erweitern Sie auf der Symbolleiste das Dropdownmenü Konfiguration, und wählen Sie Ihren Zielkonfigurationstyp aus:
- Aktivieren Sie für eine 64-Bit-Python-Runtime die Konfiguration x64.
- Aktivieren Sie für eine 32-Bit-Python-Runtime die Konfiguration Win32.
Wiederholen Sie diese Schritte unbedingt für beide Projekte.
Konfigurieren der Projekteigenschaften
Bevor Sie den neuen C++-Dateien Code hinzufügen, konfigurieren Sie die Eigenschaften für jedes C++-Modulprojekt und testen Sie die Konfigurationen, um sicherzustellen, dass alles funktioniert.
Sie müssen die Projekteigenschaften sowohl für die Debug- als auch die Release-Build-Konfigurationen jedes Moduls festlegen.
Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das C++-Modulprojekt (superfastcode oder superfastcode2), und wählen Sie Eigenschaften aus.
Konfigurieren Sie die Eigenschaften für den Debug-Build des Moduls, und konfigurieren Sie dann die gleichen Eigenschaften für den Release-Build:
Konfigurieren Sie oben im Projekt-Dialogfeld Eigenschaftenseiten die folgenden Dateikonfigurationsoptionen:
Wählen Sie für die Konfiguration die Option Debuggen oder Release aus. (Möglicherweise werden diese Optionen mit dem Präfix Aktiv angezeigt).
Wählen Sie für die Plattform die Option Aktiv (x64) oder Aktiv (Win32) aus, abhängig von Ihrer Auswahl im vorherigen Schritt.
Hinweis
Wenn Sie Ihre eigenen Projekte erstellen, sollten Sie die Debug- und Release-Konfigurationen gemäß Ihren spezifischen Szenarioanforderungen separat konfigurieren. In dieser Übung legen Sie die Konfigurationen für die Verwendung eines Release-Builds von CPython fest. In dieser Konfiguration sind einige Debugfunktionen der C++-Runtime deaktiviert, z. B. Assertionen. Die Verwendung der CPython-Debugbinärdateien (python_d.exe) erfordert andere Einstellungen.
Legen Sie weitere Projekteigenschaften wie in der folgenden Tabelle beschrieben fest.
Um einen Eigenschaftswert zu ändern, geben Sie einen Wert in das Eigenschaftsfeld ein. Bei einigen Feldern können Sie den aktuellen Wert auswählen, um ein Dropdownmenü mit Auswahlmöglichkeiten zu erweitern, oder ein Dialogfeld öffnen, um beim Definieren des Werts zu helfen.
Nachdem Sie Werte auf einer Registerkarte aktualisiert haben, wählen Sie Übernehmen aus, bevor Sie zu einer anderen Registerkarte wechseln. Mit dieser Aktion können Sie sicherstellen, dass Ihre Änderungen erneut Standard werden.
Registerkarte und Abschnitt Eigenschaft Wert Konfigurationseigenschaften>Allgemein Zielname Geben Sie den Namen des Moduls so an, wie er von Python in from...import
-Anweisungen wie superfastcode verwendet werden soll. Sie verwenden diesen Namen im C++-Code, wenn Sie das Modul für Python definieren. Wenn Sie den Namen des Projekts als Modulnamen verwenden möchten, behalten Sie den Standardwert $<ProjectName> bei. Fügen Sie fürpython_d.exe
außerdem am Ende der Datei_d
hinzu.Konfigurationstyp Dynamische Bibliothek (.dll) Konfigurationseigenschaften>Erweitert Zieldateierweiterung .pyd (Python-Erweiterungsmodul) C/C++->Allgemein Zusätzliche Includeverzeichnisse Fügen Sie den entsprechenden include-Ordner von Python für Ihre Installation hinzu, z. B. c:\Python36\include. C/C++->Präprozessor Präprozessordefinitionen Ändern Sie den Wert für _DEBUG in NDEBUG (sofern vorhanden), damit er mit der Nicht-Debugversion von CPython übereinstimmt. Lassen Sie diesen Wert unverändert, wenn Sie python_d.exe verwenden. C/C++>Codegenerierung Laufzeitbibliothek Multithread-DLL (/MD), damit er mit der Nicht-Debugversion des Release von CPython übereinstimmt. Belassen Sie diesen Wert auf Multithread-Debug-DLL (/MDd), wenn Sie python_d.exe verwenden. Grundlegende Laufzeitüberprüfungen Standard Linker>Allgemein Zusätzliche Bibliotheksverzeichnisse Fügen Sie den entsprechenden libs-Ordner von Python für Ihre Installation hinzu, der .lib-Dateien enthält, z. B. c:\Python36\libs. Achten Sie darauf, dass Sie auf den libs-Ordner verweisen, der .lib-Dateien enthält und nicht auf den Lib-Ordner, der .py-Dateien enthält. Wichtig
Wenn die Registerkarte C/C++ nicht als Option für die Projekteigenschaften angezeigt wird, enthält das Projekt keine Code-Dateien, die Visual Studio als C/C++-Quelldateien erkennt. Dieser Zustand kann eintreten, wenn Sie eine Quelldatei ohne eine .c- oder .cpp-Dateierweiterung erstellen.
Wenn Sie beim Erstellen der C++-Datei versehentlich module.coo anstelle von module.cpp eingegeben haben, erstellt Visual Studio die Datei, legt den Dateityp aber nicht auf C/C+-Compiler fest. Dieser Dateityp ist erforderlich, um das Vorhandensein der Registerkarte „C/C++-Eigenschaften“ im Dialogfeld „Projekteigenschaften“ zu aktivieren. Die falsche Identifizierung bleibt auch dann bestehen, wenn Sie die Codedatei mit einer .cpp-Dateierweiterung umbenennen.
Um die Codedatei richtig festzulegen, klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf die Code-Datei, und wählen Sie Eigenschaften aus. Wählen Sie für den Elementtyp die Option C/C++-Compiler aus.
Nachdem Sie alle Eigenschaften aktualisiert haben, wählen Sie OK aus.
Wiederholen Sie die Schritte für die andere Build-Konfiguration.
Testen Sie Ihre aktuelle Konfiguration. Wiederholen Sie die folgenden Schritte für die Debug- und Release-Builds beider C++-Projekte.
Legen Sie die Build-Konfiguration auf der Visual Studio-Symbolleiste auf Debuggen oder Release fest:
Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das C++-Projekt, und wählen Sie Build aus.
Die .pyd-Dateien sind im Projektmappenordner unter Debuggen und Release und nicht im C++-Projektordner selbst zu finden.
Hinzufügen von Code und Testkonfiguration
Jetzt können Sie Ihren C++-Dateien Code hinzufügen und den Release-Build testen.
Öffnen Sie für das C++-Projekt superfastcode die module.cpp-Datei im Code-Editor.
Fügen Sie in die module.cpp-Datei den folgenden Code ein:
#include <Windows.h> #include <cmath> const double e = 2.7182818284590452353602874713527; double sinh_impl(double x) { return (1 - pow(e, (-2 * x))) / (2 * pow(e, -x)); } double cosh_impl(double x) { return (1 + pow(e, (-2 * x))) / (2 * pow(e, -x)); } double tanh_impl(double x) { return sinh_impl(x) / cosh_impl(x); }
Speichern Sie die Änderungen.
Erstellen Sie die Release-Konfiguration für das C++-Projekt, um zu bestätigen, dass Ihr Code korrekt ist.
Wiederholen Sie die Schritte zum Hinzufügen von Code zur C++-Datei für das superfastcode2-Projekt, und testen Sie den Release-Build.
Konvertieren von C++-Projekten in Python-Erweiterungen
Sie müssen die exportierten Methoden zunächst so anpassen, dass Sie mit den Python-Typen interagieren, um die C++-DLL in eine Python-Erweiterung konvertieren zu können. Fügen Sie dann eine Funktion zum Exportieren des Moduls zusammen mit Definitionen für die Methoden des Moduls hinzu.
In den folgenden Abschnitten wird veranschaulicht, wie Sie die Erweiterungen mithilfe der CPython-Erweiterungen und pyBind11 erstellen. Das superfasctcode-Projekt verwendet die CPython-Erweiterungen und das superfasctcode2-Projekt implementiert PyBind11.
Verwendung von CPython-Erweiterungen
Weitere Informationen zu dem in diesem Abschnitt dargestellten Code finden Sie im Python/C-API-Referenzhandbuch und insbesondere auf der Seite Modulobjekte. Wenn Sie den Referenzinhalt überprüfen, achten Sie darauf, Ihre Python-Version in der Dropdownliste oben rechts auszuwählen.
Öffnen Sie für das C++-Projekt superfastcode die module.cpp-Datei im Code-Editor.
Fügen Sie oben in der module.cpp-Datei eine Anweisung hinzu, um die Python.h-Headerdatei einzuschließen:
#include <Python.h>
Ersetzen Sie den
tanh_impl
-Methodencode, um Python-Typen (d. h.PyObject*
) zu akzeptieren und zurückzugeben:PyObject* tanh_impl(PyObject* /* unused module reference */, PyObject* o) { double x = PyFloat_AsDouble(o); double tanh_x = sinh_impl(x) / cosh_impl(x); return PyFloat_FromDouble(tanh_x); }
Fügen Sie am Ende der Datei eine Struktur hinzu, um zu definieren, wie die C++-Funktion
tanh_impl
in Python dargestellt wird:static PyMethodDef superfastcode_methods[] = { // The first property is the name exposed to Python, fast_tanh // The second is the C++ function with the implementation // METH_O means it takes a single PyObject argument { "fast_tanh", (PyCFunction)tanh_impl, METH_O, nullptr }, // Terminate the array with an object containing nulls { nullptr, nullptr, 0, nullptr } };
Fügen Sie eine weitere Struktur hinzu, um zu definieren, wie Sie in Ihrem Python-Code auf das Modul verweisen, insbesondere wenn Sie die
from...import
-Anweisung verwenden.Der Name, der in diesem Code importiert wird, sollte mit dem Wert in den Projekteigenschaften unter Konfigurationseinstellungen>Allgemein>Zielname übereinstimmen.
Im folgenden Beispiel bedeutet der Name
"superfastcode"
, dass Sie diefrom superfastcode import fast_tanh
-Anweisung in Python verwenden können, dafast_tanh
insuperfastcode_methods
definiert ist. Dateinamen, die für das C++-Projekt intern sind, z. B. module.cpp, sind unlogisch.static PyModuleDef superfastcode_module = { PyModuleDef_HEAD_INIT, "superfastcode", // Module name to use with Python import statements "Provides some functions, but faster", // Module description 0, superfastcode_methods // Structure that defines the methods of the module };
Fügen Sie eine Methode hinzu, die Python beim Laden des Moduls aufruft. Der Methodenname muss
PyInit_<module-name>
sein, wobei <module-name> genau der Eigenschaft Konfigurationseigenschaften>Allgemein>Zielname des C++-Projekts entspricht. Das heißt, der Methodenname entspricht dem Dateinamen der .pyd-Datei, die vom Projekt erstellt wird.PyMODINIT_FUNC PyInit_superfastcode() { return PyModule_Create(&superfastcode_module); }
Erstellen Sie das C++-Projekt, und überprüfen Sie Ihren Code. Lesen Sie bei Fehlern den Abschnitt „Problembehandlung bei Kompilierungsfehlern“.
Verwendung von PyBind11
Wenn Sie die Schritte im vorherigen Abschnitt für das superfastcode-Projekt ausführen, stellen Sie möglicherweise fest, dass für die Übung Codebausteine erforderlich sind, um die Modulstrukturen für C++-CPython-Erweiterungen zu erstellen. In dieser Übung entdecken Sie, dass PyBind11 den Codierungsprozess vereinfacht. Sie verwenden Makros in einer C++-Headerdatei, um das gleiche Ergebnis zu erzielen, jedoch mit viel weniger Code. Zusätzliche Schritte sind jedoch erforderlich, um sicherzustellen, dass Visual Studio die PyBind11-Bibliotheken finden und Dateien enthalten kann. Weitere Informationen zum Code in diesem Abschnitt finden Sie in den PyBind11-Grundlagen.
Installieren von PyBind11
Der erste Schritt besteht darin, PyBind11 in Ihrer Projektkonfiguration zu installieren. In dieser Übung verwenden Sie das PowerShell-Entwicklerfenster .
Öffnen Sie Extras>Befehlszeile>PowerShell-Entwicklerfenster.
Installieren Sie im PowerShell-Entwicklerfenster PyBind11 durch Verwendung des pip-Befehls
pip install pybind11
oderpy -m pip install pybind11
.Visual Studio installiert PyBind11 und dessen abhängige Pakete.
Hinzufügen von PyBind11-Pfaden zum Projekt
Nach der Installation von PyBind11 müssen Sie die PyBind11-Pfade zur Eigenschaft Zusätzliche Includeverzeichnisse für das Projekt hinzufügen.
Führen Sie im PowerShell-Entwicklerfenster den Befehl
python -m pybind11 --includes
oderpy -m pybind11 --includes
aus.Diese Aktion druckt eine Liste der PyBind11-Pfade, die Sie zu Ihren Projekteigenschaften hinzufügen müssen.
Markieren Sie die Liste der Pfade im Fenster, und wählen Sie Kopieren (Doppelseite) auf der Fenstersymbolleiste aus.
Die Liste der verketteten Pfade wird Ihrer Zwischenablage hinzugefügt.
Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Projekt superfastcode2, und wählen Sie Eigenschaften aus.
Wählen Sie oben im Dialogfeld Eigenschaftenseiten für das Feld Konfiguration die Option Release aus. (Möglicherweise wird diese Optionen mit dem Präfix Aktiv angezeigt).
Erweitern Sie im Dialogfeld auf der Registerkarte C/C++>Allgemein das Dropdownmenü für die Eigenschaft Zusätzliche Includeverzeichnisse, und wählen Sie Bearbeiten aus.
Fügen Sie im Popupdialogfeld die Liste der kopierten Pfade hinzu:
Wiederholen Sie diese Schritte für jeden Pfad in der aus dem Fenster PowerShell-Entwicklerfenster kopierten verketteten Liste:
Wählen Sie Neue Zeile (Ordner mit Pluszeichen) auf der Symbolleiste des Popupdialogfelds aus.
Visual Studio fügt oben in der Liste der Pfade eine leere Zeile hinzu und positioniert den Einfügecursor am Anfang.
Fügen Sie den PyBind11-Pfad in die leere Zeile ein.
Sie können auch Weitere Optionen (...) auswählen und ein Popupdatei-Explorer-Dialogfeld verwenden, um zum Pfadspeicherort zu navigieren.
Wichtig
- Wenn der Pfad das
-I
-Präfix enthält, entfernen Sie das Präfix aus dem Pfad. - Damit Visual Studio einen Pfad erkennt, muss sich der Pfad in einer separaten Zeile befinden.
Nachdem Sie einen neuen Pfad hinzugefügt haben, zeigt Visual Studio den bestätigten Pfad im Feld Ausgewerteter Wert an.
- Wenn der Pfad das
Wählen Sie OK aus, um das Popup-Dialogfeld zu schließen.
Zeigen Sie oben im Dialogfeld Eigenschaftenseiten mit dem Mauszeiger auf den Wert für die Eigenschaft Zusätzliche Includeverzeichnisse, und bestätigen Sie, dass PyBind11-Pfade vorhanden sind.
Wählen Sie OK aus, um die Eigenschaftsänderungen zu übernehmen.
Aktualisieren der module.cpp-Datei
Der letzte Schritt besteht darin, der C++-Projektdatei die Headerdatei „PyBind11“ und den Makrocode hinzuzufügen.
Öffnen Sie für das C++-Projekt superfastcode2 die module.cpp-Datei im Code-Editor.
Fügen Sie oben in der module.cpp-Datei eine Anweisung hinzu, um die pybind11.h-Headerdatei einzuschließen:
#include <pybind11/pybind11.h>
Fügen Sie am Ende der module.cpp-Datei Code für das
PYBIND11_MODULE
-Makro hinzu, um den Einstiegspunkt für die C++-Funktion zu definieren:namespace py = pybind11; PYBIND11_MODULE(superfastcode2, m) { m.def("fast_tanh2", &tanh_impl, R"pbdoc( Compute a hyperbolic tangent of a single argument expressed in radians. )pbdoc"); #ifdef VERSION_INFO m.attr("__version__") = VERSION_INFO; #else m.attr("__version__") = "dev"; #endif }
Erstellen Sie das C++-Projekt, und überprüfen Sie Ihren Code. Lesen Sie bei Fehlern den nächsten Abschnitt „Problembehandlung bei Kompilierungsfehlern“.
Problembehandlung von Kompilierungsfehlern
Lesen Sie sich die folgenden Abschnitte für mögliche Probleme durch, die dazu führen können, dass die Erstellung des C++-Moduls fehlschlägt.
Fehler: Die Headerdatei kann nicht gefunden werden.
Visual Studio gibt eine Fehlermeldung wie E1696: cannot open source file „Python.h“ (E1696: Die Quelldatei „Python.h“ kann nicht geöffnet werden oder C1083: Cannot open include file: "Python.h": No such file or directory (C1083: Die Includedatei „Python.h“ kann nicht geöffnet werden: Datei oder Verzeichnis nicht vorhanden.) zurück.
Dieser Fehler weist darauf hin, dass der Compiler eine erforderliche Header-Datei (.h) für Ihr Projekt nicht finden kann.
Stellen Sie für das superfastcode-Projekt sicher, dass die Projekteigenschaft C/C++>Allgemein>Zusätzliche Includeverzeichnisse den Pfad zum include-Ordner für Ihre Python-Installation enthält. Überprüfen Sie die Schritte in Projekteigenschaften konfigurieren.
Stellen Sie für das superfastcode2-Projekt sicher, dass dieselbe Projekteigenschaft den Pfad zum include-Ordner für Ihre PyBind11-Installation enthält. Überprüfen Sie die Schritte PyBind-Pfade zum Projekt hinzufügen.
Weitere Informationen zum Zugriff auf Ihre Konfigurationsinformationen für die Python-Installation finden Sie in der Python-Dokumentation.
Fehler: Python-Bibliotheken konnten nicht gefunden werden
Visual Studio gibt einen Fehler zurück, der darauf hinweist, dass der Compiler die erforderlichen Bibliotheksdateien (DLL) für Ihr Projekt nicht finden kann.
- Stellen Sie für das C++-Projekt (superfastcode oder superfastcode2) sicher, dass die Eigenschaft Linker>Allgemein>Zusätzliche Bibliotheksverzeichnisse den Pfad zum libs-Ordner für Ihre Python-Installation enthält. Überprüfen Sie die Schritte in Projekteigenschaften konfigurieren.
Weitere Informationen zum Zugriff auf Ihre Konfigurationsinformationen für die Python-Installation finden Sie in der Python-Dokumentation.
Linkerfehler im Zusammenhang mit der Zielarchitektur
Visual Studio meldet Linkerfehler im Zusammenhang mit der Zielarchitekturkonfiguration für Ihr Projekt, z. B. x64 oder Win32.
- Ändern Sie für das C++-Projekt (superfastcode oder superfastcode2) die Zielkonfiguration entsprechend Ihrer Python-Installation. Wenn die Zielkonfiguration Ihres C++-Projekts beispielsweise Win32 ist, Ihre Python-Installation jedoch 64-Bit, ändern Sie die Zielkonfiguration des C++-Projekts in x64.
Testen des Codes und Vergleichen der Ergebnisse
Da Sie nun die DLLs als Python-Erweiterungen strukturiert haben, können Sie sich vom Python-Projekt auf sie beziehen, die Module importieren und ihre Methoden verwenden.
Zur Verfügung stellen von DLL für Python
Sie können Ihre DLL auf verschiedene Arten für Python verfügbar machen. Hier sind zwei Optionen, die Sie in Betracht ziehen sollten:
Wenn sich Ihr Python-Projekt und das C++-Projekt in derselben Lösung befinden, können Sie den folgenden Ansatz verwenden:
Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf den Knoten Referenzen in Ihrem Python-Projekt, und wählen Sie Referenz hinzufügen aus.
Stellen Sie sicher, dass Sie diese Aktion für Ihr Python-Projekt und nicht für Ihr C++-Projekt ausführen.
Erweitern Sie im Dialogfeld Referenz hinzufügen die Registerkarte Projekte.
Aktivieren Sie die Kontrollkästchen für die superfastcode- und superfastcode2-Projekte, und wählen Sie OK aus.
Ein alternativer Ansatz besteht darin, das C++-Erweiterungsmodul in Ihrer Python-Umgebung zu installieren. Diese Methode stellt das Modul anderen Python-Projekten zur Verfügung. Weitere Informationen finden Sie in der Dokumentation zum setuptools-Projekt.
Führen Sie die folgenden Schritte aus, um das C++-Erweiterungsmodul in Ihrer Python-Umgebung zu installieren:
Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf Ihr C++-Projekt, und wählen Sie Hinzufügen>Neues Element aus.
Wählen Sie in der Liste der Dateivorlagen die C++-Datei (.cpp) aus.
Geben Sie den Namen für die Datei als setup.py ein, und wählen Sie dann Hinzufügen aus.
Achten Sie darauf, den Dateinamen mit der Python-Erweiterung (.py) einzugeben. Visual Studio erkennt die Datei trotz der Verwendung der C++-Dateivorlage als Python-Code.
Visual Studio öffnet die neue Datei im Code-Editor.
Fügen Sie den folgenden Code in die neue Datei ein. Wählen Sie die Codeversion aus, die Ihrer Erweiterungsmethode entspricht:
CPython-Erweiterungen (superfastcode-Projekt):
from setuptools import setup, Extension sfc_module = Extension('superfastcode', sources = ['module.cpp']) setup( name='superfastcode', version='1.0', description='Python Package with superfastcode C++ extension', ext_modules=[sfc_module] )
PyBind11 (superfastcode2-Projekt):
from setuptools import setup, Extension import pybind11 cpp_args = ['-std=c++11', '-stdlib=libc++', '-mmacosx-version-min=10.7'] sfc_module = Extension( 'superfastcode2', sources=['module.cpp'], include_dirs=[pybind11.get_include()], language='c++', extra_compile_args=cpp_args, ) setup( name='superfastcode2', version='1.0', description='Python package with superfastcode2 C++ extension (PyBind11)', ext_modules=[sfc_module], )
Erstellen Sie im C++-Projekt eine zweite Datei mit dem Namen pyproject.toml und fügen Sie den folgenden Code ein:
[build-system] requires = ["setuptools", "wheel", "pybind11"] build-backend = "setuptools.build_meta"
Die TOML-Datei (.toml) verwendet das Tom's Obvious, Minimal Language-Format für Konfigurationsdateien.
Klicken Sie zum Erstellen der Erweiterung mit der rechten Maustaste auf den Dateinamen pyproject.toml auf der Registerkarte des Codefensters, und wählen Sie dann Vollständigen Pfad kopieren aus.
Sie löschen den Namen pyproject.toml vor der Verwendung aus dem Pfad.
Erweitern Sie im Projektmappen-Explorer den Knoten Python-Umgebungen für die Lösung.
Klicken Sie mit der rechten Maustaste auf die aktiven Python-Umgebungen (in Fettformatierung dargestellt), und wählen Sie Python-Pakete verwalten aus.
Der Bereich Python-Umgebungen wird geöffnet.
Wenn das erforderliche Paket bereits installiert ist, wird es in diesem Bereich aufgeführt.
- Bevor Sie fortfahren, wählen Sie das X neben dem Paketnamen aus, um es zu deinstallieren.
Fügen Sie im Suchfeld für den Bereich Python-Umgebungen den kopierten Pfad ein, und löschen Sie den Dateinamen pyproject.toml am Ende des Pfads.
Klicken Sie auf die Eingabetaste, um das Modul über den Speicherort des kopierten Pfads zu installieren.
Tipp
Wenn die Installation aufgrund eines Berechtigungsfehlers fehlschlägt, fügen Sie das
--user
-Argument am Ende des Befehls hinzu, und wiederholen Sie die Installation.
Aufrufen der DLL von Python
Nachdem Sie die DLL für Python verfügbar gemacht haben, wie im vorherigen Abschnitt beschrieben, können Sie die superfastcode.fast_tanh
- und superfastcode2.fast_tanh2
-Funktionen aus Python aufrufen. Anschließend können Sie die Funktionsleistung mit der Python-Implementierung vergleichen.
Führen Sie die folgenden Schritte aus, um die Erweiterungsmodul-DLL aus Python aufzurufen:
Öffnen Sie die .py-Datei für Ihr Python-Projekt im Code-Editor.
Fügen Sie am Ende der Datei den folgenden Code hinzu, um die aus den DLLs exportierten Methoden aufzurufen und ihre Ausgabe anzuzeigen:
from superfastcode import fast_tanh test(lambda d: [fast_tanh(x) for x in d], '[fast_tanh(x) for x in d] (CPython C++ extension)') from superfastcode2 import fast_tanh2 test(lambda d: [fast_tanh2(x) for x in d], '[fast_tanh2(x) for x in d] (PyBind11 C++ extension)')
Führen Sie das Python-Programm aus, indem Sie Debuggen>Ohne Debuggen starten auswählen, oder die Tastenkombination STRG+F5 verwenden.
Hinweis
Wenn der Befehl Starten ohne Debuggen nicht verfügbar ist, klicken Sie mit der rechten Maustaste im Projektmappen-Explorer auf das Python-Projekt, und wählen Sie Als Startprojekt festlegen aus.
Beachten Sie beim Ausführen des Programms, dass die C++-Routinen ungefähr 5 bis 20 Mal schneller ausgeführt werden als die Python-Implementierung.
Hier ist ein Beispiel für die typische Programmausgabe:
Running benchmarks with COUNT = 500000 [tanh(x) for x in d] (Python implementation) took 0.758 seconds [fast_tanh(x) for x in d] (CPython C++ extension) took 0.076 seconds [fast_tanh2(x) for x in d] (PyBind11 C++ extension) took 0.204 seconds
Versuchen Sie die
COUNT
-Variable zu vergrößern, sodass die Zeitunterschiede deutlicher werden.Beachten Sie, dass ein Debug-Build des C++-Moduls zudem langsamer als ein Release-Build ausgeführt wird, da der Debug-Build weniger optimiert ist und verschiedene Fehlerprüfungen enthält. Versuchen Sie, zum Vergleich zwischen den Build-Konfigurationen zu wechseln. Denken Sie jedoch daran, die Eigenschaften zu aktualisieren, die Sie zuvor für die Release-Konfiguration festgelegt haben.
Berücksichtigen von Prozessgeschwindigkeit und Overhead
In der Ausgabe können Sie sehen, dass die PyBind11-Erweiterung zwar nicht so schnell wie die CPython-Erweiterung, jedoch schneller als die normale Python-Implementierung ist. Der Hauptgrund für den Unterschied liegt in der Verwendung des METH_O-Flags. Dieses Flag unterstützt nicht mehrere Parameter, Parameternamen oder Schlüsselwortargumente. PyBind11 generiert einen geringfügig komplexeren Code, um Aufrufern eine Python-ähnlichere Schnittstelle zur Verfügung zu stellen. Da der Testcode die Funktion 500.000 Mal aufruft, können die Ergebnisse den Overhead erheblich erhöhen.
Sie können den Mehraufwand weiter reduzieren, indem Sie die for
-Schleife in nativem Python-Code verschieben. Diese Vorgehensweise umfasst die Verwendung des Iteratorprotokolls (oder des PyBind11-Typs py::iterable
für den Funktionsparameter), um die einzelnen Elemente zu verarbeiten. Das Entfernen der wiederholten Übergänge zwischen Python und C++ ist eine effektive Möglichkeit, um den Zeitaufwand für die Verarbeitung der Sequenz zu verringern.
Behandeln von Importproblemen
Wenn Sie beim Importieren des Moduls eine ImportError
-Meldung erhalten, haben Sie folgende Möglichkeiten, diesen Fehler zu beheben:
Wenn Sie über einen Projektverweis erstellen, stellen Sie sicher, dass Ihre C++-Projekteigenschaften mit der Python-Umgebung übereinstimmen, die für Ihr Python-Projekt aktiviert ist. Vergewissern Sie sich, dass die gleichen Ordnerspeicherorte für die Dateien Include (.h) und Library (DLL) verwendet werden.
Stellen Sie sicher, dass die Ausgabedatei richtig benannt ist, z. B. superfastcode.pyd. Ein falscher Name oder eine falsche Erweiterung verhindern den Import der erforderlichen Datei.
Wenn Sie Ihr Modul mithilfe der Datei setup.py installiert haben, stellen Sie sicher, dass Sie den
pip
-Befehl in der Python-Umgebung ausführen, die für Ihr Python-Projekt aktiviert ist. Wenn Sie die aktive Python-Umgebung für Ihr Projekt in Projektmappen-Explorer erweitern, sollte ein Eintrag für das C++-Projekt angezeigt werden, z. B. superfastcode.
Debuggen von C++-Code
Visual Studio unterstützt das gemeinsame Debuggen von Python- und C++-Code. Die folgenden Schritte veranschaulichen den Debugprozess für das C++-Projekt superfastcode, aber der Prozess ist für das superfastcode2-Projekt identisch.
Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Python-Projekt, und wählen Sie Eigenschaften aus.
Wählen Sie im Bereich Eigenschaften die Registerkarte Debuggen und dann die Option Debuggen>Debuggen von nativem Code aktivieren aus.
Tipp
Wenn Sie das Debuggen von nativem Code aktivieren, wird das Python-Ausgabefenster möglicherweise sofort nach Abschluss des Programms geschlossen, ohne anzuhalten und die Eingabeaufforderung Drücken Sie eine beliebige Taste, um fortzufahren... anzuzeigen. Um das Anhalten und die Eingabeaufforderung zu erzwingen, nachdem Sie das Debuggen von systemeigenem Code aktiviert haben, fügen Sie das Argument
-i
dem Feld Ausführen>Interpreterargumente auf der Registerkarte Debuggen hinzu. Dieses Argument versetzt den Python-Interpreter nach der Codeausführung in den interaktiven Modus. Das Programm wartet darauf, dass Sie STRG+Z+Eingabetaste drücken, um das Fenster zu schließen. Ein alternativer Ansatz besteht darin, am Ende Ihres Python-Programmsimport os
- undos.system("pause")
-Anweisungen hinzuzufügen. Durch diesen Code wird die ursprüngliche Aufforderung zur Pause dupliziert.Wählen Sie Datei>Speichern (oder STRG+S) aus, um die Eigenschaftsänderungen zu speichern.
Legen Sie auf der Visual Studio-Symbolleiste die Build-Konfiguration auf Debuggen fest.
Da die Codeausführung im Debugger in der Regel länger dauert, sollten Sie die
COUNT
-Variable in der .py-Datei Ihres Python-Projekts auf einen fünfmal niedrigeren Wert als dem Standardwert festlegen. Ändern Sie ihn beispielsweise von 500000 in 100000.Legen Sie in Ihrem C++-Code einen Haltepunkt in der ersten Zeile der
tanh_impl
-Methode fest.Starten Sie den Debugger, indem Sie Debuggen>Debuggen starten auswählen, oder die Tastenkombination F5 verwenden.
Der Debugger wird angehalten, wenn der Code am Haltepunkt aufgerufen wird. Überprüfen Sie, ob die Konfiguration auf Debuggen festgelegt ist und Sie das Projekt gespeichert haben, wenn der Haltepunkt nicht erreicht wird. Dies geschieht beim Starten des Debuggers nicht automatisch.
Am Haltepunkt können Sie den C++-Code schrittweise durchgehen, Variablen prüfen, usw. Weitere Informationen zu diesen Features finden Sie unter Gleichzeitiges Debuggen von Python und C++.
Alternative Ansätze
Wie in der folgenden Tabelle beschrieben, gibt es verschiedene Möglichkeiten zum Erstellen von Python-Erweiterungen. In diesem Artikel werden die ersten beiden Zeilen, CPython
und PyBind11
, erläutert.
Vorgehensweise | Vintage | Maßgebliche Nutzer |
---|---|---|
C/C++-Erweiterungsmodule für CPython |
1991 | Standardbibliothek |
PyBind11 (empfohlen für C++) | 2015 | |
Cython (empfohlen für C) | 2007 | gevent, kivy |
HPy | 2019 | |
mypyc | 2017 | |
ctypes | 2003 | oscrypto |
cffi | 2013 | cryptography, pypy |
SWIG | 1996 | crfsuite |
Boost.Python | 2002 | |
cppyy | 2017 |