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.

    Screenshot of a list of Python development options, highlighting the Python native development tools option.

    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.

  1. Erstellen Sie ein neues Python-Projekt in Visual Studio, indem Sie Datei>Neu>Projekt auswählen.

  2. Suchen Sie im Dialogfeld Neues Projekt erstellen nach python. Wählen Sie die Vorlage Python-Anwendung und anschließend Weiter aus.

  3. 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.

  4. 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)')
    
  5. 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.

  6. 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.

  7. 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.

  8. 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.

  1. 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).

  2. Legen Sie im Dialogfenster Neues Projekt hinzufügen den Filter Sprache auf C++ fest, und geben Sie Leer im Suchfeld ein.

  3. Wählen Sie in der Liste der Projektvorlagenergebnisse Leeres Projekt und dann Weiter aus.

  4. 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.
  5. 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.

  1. 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.

  2. Wählen Sie in der Liste der Dateivorlagen die C++-Datei (.cpp) aus.

  3. 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.

  4. Erweitern Sie auf der Symbolleiste das Dropdownmenü Konfiguration, und wählen Sie Ihren Zielkonfigurationstyp aus:

    Screenshot that shows how to set the target configuration type for the C++ project in Visual Studio.

    • 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.

  1. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das C++-Modulprojekt (superfastcode oder superfastcode2), und wählen Sie Eigenschaften aus.

  2. 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:

    Screenshot that shows how to set the project build and platform options for the C++ file in Visual Studio.

    1. Wählen Sie für die Konfiguration die Option Debuggen oder Release aus. (Möglicherweise werden diese Optionen mit dem Präfix Aktiv angezeigt).

    2. 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.

    3. 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ür python_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.

    4. Nachdem Sie alle Eigenschaften aktualisiert haben, wählen Sie OK aus.

    Wiederholen Sie die Schritte für die andere Build-Konfiguration.

  3. Testen Sie Ihre aktuelle Konfiguration. Wiederholen Sie die folgenden Schritte für die Debug- und Release-Builds beider C++-Projekte.

    1. Legen Sie die Build-Konfiguration auf der Visual Studio-Symbolleiste auf Debuggen oder Release fest:

      Screenshot that shows how to set the build configuration for the C++ project in Visual Studio.

    2. 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.

  1. Öffnen Sie für das C++-Projekt superfastcode die module.cpp-Datei im Code-Editor.

  2. 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);
    }
    
  3. Speichern Sie die Änderungen.

  4. 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.

  1. Öffnen Sie für das C++-Projekt superfastcode die module.cpp-Datei im Code-Editor.

  2. Fügen Sie oben in der module.cpp-Datei eine Anweisung hinzu, um die Python.h-Headerdatei einzuschließen:

    #include <Python.h>
    
  3. 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);
    }
    
  4. 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 }
    };
    
  5. 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 die from superfastcode import fast_tanh-Anweisung in Python verwenden können, da fast_tanh in superfastcode_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
    };
    
  6. 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);
    }
    
  7. 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 .

  1. Öffnen Sie Extras>Befehlszeile>PowerShell-Entwicklerfenster.

  2. Installieren Sie im PowerShell-Entwicklerfenster PyBind11 durch Verwendung des pip-Befehls pip install pybind11 oder py -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.

  1. Führen Sie im PowerShell-Entwicklerfenster den Befehl python -m pybind11 --includes oder py -m pybind11 --includes aus.

    Diese Aktion druckt eine Liste der PyBind11-Pfade, die Sie zu Ihren Projekteigenschaften hinzufügen müssen.

  2. Markieren Sie die Liste der Pfade im Fenster, und wählen Sie Kopieren (Doppelseite) auf der Fenstersymbolleiste aus.

    Screenshot that shows how to highlight and copy the list of paths from the Developer PowerShell window in Visual Studio.

    Die Liste der verketteten Pfade wird Ihrer Zwischenablage hinzugefügt.

  3. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Projekt superfastcode2, und wählen Sie Eigenschaften aus.

  4. 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).

  5. 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.

  6. 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:

    1. Wählen Sie Neue Zeile (Ordner mit Pluszeichen) auf der Symbolleiste des Popupdialogfelds aus.

      Screenshot that shows how to add a PyBind11 path to the Additional Include Directories property.

      Visual Studio fügt oben in der Liste der Pfade eine leere Zeile hinzu und positioniert den Einfügecursor am Anfang.

    2. 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.

  7. Wählen Sie OK aus, um das Popup-Dialogfeld zu schließen.

  8. 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.

  9. 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.

  1. Öffnen Sie für das C++-Projekt superfastcode2 die module.cpp-Datei im Code-Editor.

  2. Fügen Sie oben in der module.cpp-Datei eine Anweisung hinzu, um die pybind11.h-Headerdatei einzuschließen:

    #include <pybind11/pybind11.h>
    
  3. 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
    }
    
  4. 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.

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:

  1. 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.

  2. Erweitern Sie im Dialogfeld Referenz hinzufügen die Registerkarte Projekte.

  3. Aktivieren Sie die Kontrollkästchen für die superfastcode- und superfastcode2-Projekte, und wählen Sie OK aus.

    Screenshot that shows how to add a reference to the super fast code project in Visual Studio.

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:

  1. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf Ihr C++-Projekt, und wählen Sie Hinzufügen>Neues Element aus.

  2. Wählen Sie in der Liste der Dateivorlagen die C++-Datei (.cpp) aus.

  3. 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.

  4. 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],
      )
      
  5. 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.

  6. 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.

    Screenshot that shows how to copy the full path to the py project toml file in Visual Studio.

    Sie löschen den Namen pyproject.toml vor der Verwendung aus dem Pfad.

  7. Erweitern Sie im Projektmappen-Explorer den Knoten Python-Umgebungen für die Lösung.

  8. 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.

    Screenshot that shows how to uninstall a package in the Python Environments pane.

  9. 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.

    Screenshot that shows how to enter the path in the Python Environments pane to install the extension module.

  10. 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:

  1. Öffnen Sie die .py-Datei für Ihr Python-Projekt im Code-Editor.

  2. 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)')
    
  3. 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
    
  4. 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.

  1. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Python-Projekt, und wählen Sie Eigenschaften aus.

  2. 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-Programms import os- und os.system("pause")-Anweisungen hinzuzufügen. Durch diesen Code wird die ursprüngliche Aufforderung zur Pause dupliziert.

  3. Wählen Sie Datei>Speichern (oder STRG+S) aus, um die Eigenschaftsänderungen zu speichern.

  4. Legen Sie auf der Visual Studio-Symbolleiste die Build-Konfiguration auf Debuggen fest.

  5. 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.

  6. Legen Sie in Ihrem C++-Code einen Haltepunkt in der ersten Zeile der tanh_impl-Methode fest.

  7. 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.

    Screenshot of C++ code that contains a breakpoint in Visual Studio.

  8. 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