Erstellen einer C++-Erweiterung für PythonCreate a C++ extension for Python

Module, die in C++ (oder C#) geschrieben werden, werden häufig verwendet, um die Funktionen eines Python-Übersetzers zu erweitern sowie den Zugriff auf Fähigkeiten des Betriebssystem auf niedriger Ebene zu aktivieren.Modules written in C++ (or C) are commonly used to extend the capabilities of a Python interpreter as well as to enable access to low-level operating system capabilities. Es gibt drei Haupttypen von Modulen:There are three primary types of modules:

  • Accelerator-Modul: Da Python eine interpretierte Sprache ist, können bestimmte Teile des Codes in C++ für höhere Leistung geschrieben werden.Accelerator modules: because Python is an interpreted language, certain pieces of code can be written in C++ for higher performance.
  • 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.Wrapper modules: expose existing C/C++ interfaces to Python code or expose a more "Pythonic" API that's easy to use from Python.
  • Systemzugriffsmodule auf niedriger Ebene: Diese werden erstellt, um auf Features der CPython-Laufzeit auf niedriger Ebene, auf das Betriebssystem oder auf die zugrunde liegende Hardware zuzugreifen.Low-level system access modules: created to access lower-level features of the CPython runtime, the operating system, or the underlying hardware.

Dieser Artikel führt Sie durch die Erstellung eines C++-Erweiterungsmoduls für CPython, das einen Hyperbeltangens berechnet und diesen aus dem Python-Code abruft.This article walks through building a C++ extension module for CPython that computes a hyperbolic tangent and calls it from Python code. Die Routine wird zuerst in Python implementiert, um den relativen Leistungsgewinn durch Implementieren derselben Routine in C++ zu veranschaulichen.The routine is implemented first in Python to demonstrate the relative performance gain of implementing the same routine in C++.

Dieser Artikel veranschaulicht auch zwei Möglichkeiten zum Verfügbarmachen von C++ in Python:This article also demonstrates two ways to make the C++ available to Python:

  • Die CPython-Standarderweiterungen, die in der Python-Dokumentation beschrieben werden.The standard CPython extensions as described in the Python documentation
  • PyBind11, das aufgrund der Einfachheit für C++ 11 empfohlen wird.PyBind11, which is recommended for C++ 11 because of its simplicity.

Ein Vergleich dieser beiden und anderer Maßnahmen finden Sie unter Alternative Ansätze am Ende dieses Artikels.A comparison between these and other means is found under alternative approaches at the end of this article.

Das abgeschlossene Beispiel aus dieser exemplarischen Vorgehensweise finden Sie unter python-samples-vs-cpp-extension (GitHub).The completed sample from this walkthrough can be found on python-samples-vs-cpp-extension (GitHub).

VoraussetzungenPrerequisites

  • Visual Studio 2017 oder höher, wobei die Workloads Desktopentwicklung mit C++ und Python-Entwicklung mit den Standardoptionen installiert werden müssen.Visual Studio 2017 or later with both the Desktop Development with C++ and Python Development workloads installed with default options.

  • Aktivieren Sie in der Workload Python-Entwicklung ebenfalls das Kontrollkästchen Native Python-Entwicklungstools auf der rechten Seite.In the Python Development workload, also select the box on the right for Python native development tools. Durch diese Option wird der Großteil der Konfigurationen eingerichtet, die in diesem Artikel beschrieben werden.This option sets up most of the configuration described in this article. (Diese Option enthält automatisch auch die C++-Workload.)(This option also includes the C++ workload automatically.)

    Auswählen der Option „Native Python-Entwicklungstools“

    Tipp

    Die Installation der Workload Data Science und analytische Anwendungen umfasst standardmäßig außerdem Python und die Option Native Python-Entwicklungstools.Installing the Data science and analytical applications workload also includes Python and the Python native development tools option by default.

Weitere Informationen finden Sie unter Install Python Support for Visual Studio (Installieren von Python-Unterstützung für Visual Studio), einschließlich der Verwendung anderer Versionen von Visual Studio.For more information, see Install Python support for Visual Studio, including using other versions of Visual Studio. Wenn Sie Python separat installieren, stellen Sie sicher, dass Sie Download debugging symbols (Debugsymbole herunterladen) und Download debug binaries (Debugbinärdateien herunterladen) unter Erweiterte Optionen im Installer auswählen.If you install Python separately, be sure to select Download debugging symbols and Download debug binaries under Advanced Options in the installer. Durch diese Option wird sichergestellt, dass Sie über die erforderlichen Debugbibliotheken verfügen, wenn Sie einen Debugbuild ausführen.This option ensures that you have the necessary debug libraries available if you choose to do a debug build.

Erstellen der Python-AnwendungCreate the Python application

  1. Erstellen Sie ein neues Python-Projekt in Visual Studio, indem Sie Datei > Neu > Projekt auswählen.Create a new Python project in Visual Studio by selecting File > New > Project. Suchen Sie nach "Python", wählen Sie die Vorlage Python Application (Python-Anwendung), weisen Sie ihr einen geeigneten Namen und Standort zu, und klicken Sie auf OK.Search for "Python", select the Python Application template, give it a suitable name and location, and select OK.

  2. Für das Arbeiten mit C++ ist die Verwendung eines 32-Bit-Python-Interpreters erforderlich (Python 3.6 oder höher wird empfohlen).Working with C++ requires that you use a 32-bit Python interpreter (Python 3.6 or above recommended). Erweitern Sie im Projektmappen-Explorer von Visual Studio den Projektknoten und anschließend den Knoten Python-Umgebungen.In the Solution Explorer window of Visual Studio, expand the project node, then expand the Python Environments node. Wenn Keine 32-Bit-Umgebung als Standardumgebung angezeigt wird (entweder in Fettdruck oder als globaler Standard gekennzeichnet), befolgen Sie die Anweisungen unter Select a Python environment for a project (Auswählen einer Python-Umgebung für ein Projekt).If you don't see a 32-bit environment as the default (either in bold, or labeled with global default), then follow the instructions on Select a Python environment for a project. Wenn Sie noch keinen 32-Bit-Interpreter installiert haben, finden Sie unter Install Python interpreters (Installieren von Python-Interpretern) weitere Informationen.If you don't have a 32-bit interpreter installed, see Install Python interpreters.

  3. Fügen Sie in der .py-Datei des Projekts den folgenden Code ein, der die Berechnung eines Hyperbeltangens durchführt (Dieser wird ohne Verwendung der mathematischen Bibliothek zum einfacheren Vergleich implementiert).In the project's .py file, paste the following code that benchmarks the computation of a hyperbolic tangent (implemented without using the math library for easier comparison). Geben Sie den Code gerne manuell ein, um einige der Bearbeitungsfunktionen von Python kennenzulernen.Feel free to enter the code manually to experience some of the Python editing features.

    from itertools import islice
    from random import random
    from time import perf_counter
    
    COUNT = 500000  # Change this value depending on the speed of your computer
    DATA = list(islice(iter(lambda: (random() - 0.5) * 3.0, None), 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)')
    
  4. Führen Sie das Programm mit Debuggen > Starten ohne Debuggen (STRG+F5) aus, um die Ergebnisse anzuzeigen.Run the program using Debug > Start without Debugging (Ctrl+F5) to see the results. Sie können die COUNT-Variable anpassen, um die Ausführungsdauer des Benchmarks zu ändern.You can adjust the COUNT variable to change how long the benchmark takes to run. Legen Sie für diese exemplarische Vorgehensweise die „Count“-Variable fest, sodass die Benchmarks jeweils etwa zwei Sekunden benötigen.For the purposes of this walkthrough, set the count so that the benchmark take around two seconds.

Tipp

Wenn Sie Benchmarks ausführen, sollten Sie immer Debuggen > Ohne Debuggen starten verwenden, um den Mehraufwand zu vermeiden, der anfällt, wenn Code innerhalb des Visual Studio-Debuggers ausgeführt wird.When running benchmarks, always use Debug > Start without Debugging to avoid the overhead incurred when running code within the Visual Studio debugger.

Erstellen der C++-HauptprojekteCreate the core C++ projects

Folgen Sie den Anweisungen in diesem Abschnitt, um zwei identische C++-Projekte namens „superfastcode“ und „superfastcode2“ zu erstellen.Follow the instructions in this section to create two identical C++ projects named "superfastcode" and "superfastcode2". Später verwenden Sie in jedem Projekt verschiedene Methoden, um den C++-Code in Python verfügbar zu machen.Later you'll use different means in each project to expose the C++ code to Python.

  1. Stellen Sie sicher, dass die Umgebungsvariable PYTHONHOME auf den Python-Interpreter festgelegt ist, den Sie verwenden möchten.Make sure the PYTHONHOME environment variable is set to the Python interpreter you want to use. Die C++-Projekte in Visual Studio basieren auf dieser Variable, um Dateien zu suchen, z.B. python.h, die bei der Erstellung von Python-Erweiterungen verwendet werden.The C++ projects in Visual Studio rely on this variable to locate files such as python.h, which are used when creating a Python extension.

  2. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf die Projektmappe, und wählen Sie Hinzufügen > Neues Projekt aus.Right-click the solution in Solution Explorer and select Add > New Project. Eine Visual Studio-Projektmappe kann sowohl Python- als auch C++-Projekte zusammen enthalten (was einer der Vorteile von Visual Studio für Python ist).A Visual Studio solution can contain both Python and C++ projects together (which is one of the advantages of using Visual Studio for Python).

  3. Suchen Sie nach „C++“, wählen Sie Leeres Projekt aus, geben Sie den Namen „superfastcode“ (bzw. „superfastcode2“ für das zweite Projekt) ein, und klicken Sie dann auf OK.Search on "C++", select Empty project, specify the name "superfastcode" ("superfastcode2" for the second project), and select OK.

    Tipp

    Wenn Native Python-Entwicklungstools in Visual Studio installiert ist, können Sie stattdessen mit der Vorlage Python-Erweiterungsmodul beginnen, die bereits vieles von dem enthält, was im Folgenden beschrieben wird.With the Python native development tools installed in Visual Studio, you can start with the Python Extension Module template instead, which has much of what's described below already in place. In dieser Vorgehensweise zeigt jedoch der Start mit einem leeren Projekt ausführlich die Erstellung des Erweiterungsmoduls.For this walkthrough, though, starting with an empty project demonstrates building the extension module step by step. Sobald Sie den Prozess verstanden haben, spart Ihnen die Vorlage Zeit beim Schreiben Ihrer eigenen Erweiterungen.Once you understand the process, the template saves you time when writing your own extensions.

  4. Erstellen Sie eine C++-Datei im neuen Projekt, indem Sie mit der rechten Maustaste auf den Knoten Quelldateien klicken, Hinzufügen > Neues Element und dann C++-Datei auswählen, es module.cpp benennen und dann auf OK klicken.Create a C++ file in the new project by right-clicking the Source Files node, then select Add > New Item, select C++ File, name it module.cpp, and select OK.

    Wichtig

    Eine Datei mit der .cpp-Erweiterung wird benötigt, um die C++-Eigenschaftsseiten in den folgenden Schritten zu aktivieren.A file with the .cpp extension is necessary to turn on the C++ property pages in the steps that follow.

  5. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das C++-Projekt, und wählen Sie Eigenschaften aus.Right-click the C++ project in Solution Explorer, select Properties.

  6. Legen Sie Konfiguration am oberen Rand des angezeigten Dialogfelds Eigenschaftenseiten auf Alle Konfigurationen und Plattform auf Win32 fest.At the top of the Property Pages dialog that appears, set Configuration to All Configurations and Platform to Win32.

  7. Legen Sie die spezifischen Eigenschaften wie in der folgenden Tabelle beschrieben fest, und klicken Sie auf OK.Set the specific properties as described in the following table, then select OK.

    RegisterkarteTab EigenschaftProperty WertValue
    AllgemeinGeneral Allgemein > ZielnameGeneral > Target Name Geben Sie den Namen des Moduls so an, wie er von Python in from...import-Anweisungen verwendet werden soll.Specify the name of the module as you want to refer to it from Python in from...import statements. Sie verwenden diesen Namen in C++, wenn Sie das Modul für Python definieren.You use this same name in the C++ when defining the module for Python. Wenn Sie den Namen des Projekts als Modulnamen verwenden möchten, behalten Sie den Standardwert $(ProjectName) bei.If you want to use the name of the project as the module name, leave the default value of $(ProjectName).
    Allgemein > ZielerweiterungGeneral > Target Extension .pyd.pyd
    Projektstandards > KonfigurationstypProject Defaults > Configuration Type Dynamische Bibliothek (.dll)Dynamic Library (.dll)
    C/C++- > AllgemeinC/C++ > General Zusätzliche IncludeverzeichnisseAdditional Include Directories Fügen Sie den für Ihre Installation geeigneten Python Add the Python include-Ordner hinzu, z.B. c:\Python36\include.Add the Python include folder as appropriate for your installation, for example, c:\Python36\include.
    C/C++- > PräprozessorC/C++ > Preprocessor PräprozessordefinitionenPreprocessor Definitions Nur CPython: Fügen Sie Py_LIMITED_API; (einschließlich des Semikolons) zum Anfang der Zeichenfolge hinzu.CPython only: add Py_LIMITED_API; to the beginning of the string (including the semicolon). Durch diese Definition werden einige Funktionen eingeschränkt, die Sie aus Python aufrufen können, und der Code wird zwischen unterschiedlichen Python-Versionen portabler.This definition restricts some of the functions you can call from Python and makes the code more portable between different versions of Python. Wenn Sie mit PyBind11 arbeiten, fügen Sie diese Definition nicht hinzu, da sonst Buildfehler angezeigt werden.If you're working with PyBind11, don't add this definition, otherwise you'll see build errors.
    C/C++ > CodegenerierungC/C++ > Code Generation LaufzeitbibliothekRuntime Library Multithreaded-DLL (/MD) (siehe Warnung unten)Multi-threaded DLL (/MD) (see Warning below)
    Linker > AllgemeinLinker > General Zusätzliche BibliotheksverzeichnisseAdditional Library Directories Fügen Sie den für Ihre Installation geeigneten Python libs-Ordner hinzu, der .lib-Dateien enthält, z.B. c:\Python36\libs.Add the Python libs folder containing .lib files as appropriate for your installation, for example, 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.)(Be sure to point to the libs folder that contains .lib files, and not the Lib folder that contains .py files.)

    Tipp

    Wenn die Registerkarte „C/C++“ nicht in den Projekteigenschaften angezeigt wird, enthält das Projekt keine Dateien, die als C/C++-Quelldateien identifiziert werden.If you don't see the C/C++ tab in the project properties, it's because the project doesn't contain any files that it identifies as C/C++ source files. Dieser Zustand kann eintreten, wenn Sie eine Quelldatei ohne eine .c- oder .cpp-Erweiterung erstellen.This condition can occur if you create a source file without a .c or .cpp extension. Wenn Sie zuvor versehentlich module.coo statt module.cpp im Dialogfeld „Neues Element“ eingegeben haben, erstellt Visual Studio die Datei, legt den Dateityp jedoch nicht auf „C/C++-Code“ fest, was die Registerkarte „C/C++-Eigenschaften“ aktiviert. Solche falschen Identifizierungen können auch auftreten, wenn Sie die Datei mit .cpp umbenennen.For example, if you accidentally entered module.coo instead of module.cpp in the new item dialog earlier, then Visual Studio creates the file but doesn't set the file type to "C/C+ Code," which is what activates the C/C++ properties tab. Such misidentification remains the case even if you rename the file with .cpp. Zum Festlegen des korrekten Dateityps klicken Sie mit der rechten Maustaste auf die Datei im Projektmappen-Explorer, wählen Eigenschaften aus und legen Dateityp auf C/C++-Code fest.To set the file type properly, right-click the file in Solution Explorer, select Properties, then set File Type to C/C++ Code.

    Warnung

    Legen Sie die Option C/C++ > Codegenerierung > Runtimebibliothek auch für eine Debugkonfiguration immer auf Multithreaded-DLL (/MD) fest, da durch diese Einstellung die Python-Binärdateien erstellt werden, die nicht für das Debuggen verwendet werden.Always set the C/C++ > Code Generation > Runtime Library option to Multi-threaded DLL (/MD), even for a debug configuration, because this setting is what the non-debug Python binaries are built with. Wenn Sie CPython verwenden und die Multithreaded-Debug-DLL (/MDd) -Option festlegen, wird beim Erstellen einer Debug-Konfiguration die folgende Fehlermeldung ausgegeben: C1189: Py_LiMITED_API is incompatible with Py_DEBUG, Py_TRACE_REFS und Py_REF_DEBUG (C1189: Py_LIMITED_API ist nicht mit Py_DEBUG, Py_TRACE_REFS und Py_REF_DEBUG kompatibel) .With CPython, if you happen to set the Multi-threaded Debug DLL (/MDd) option, building a Debug configuration produces error C1189: Py_LIMITED_API is incompatible with Py_DEBUG, Py_TRACE_REFS, and Py_REF_DEBUG. Wenn Sie darüber hinaus Py_LIMITED_API entfernen (dies ist bei CPython, aber nicht bei PyBind11 erforderlich), um den Buildfehler zu vermeiden, stürzt Python beim Versuch, das Modul zu importieren, ab.Furthermore, if you remove Py_LIMITED_API (which is required with CPython, but not PyBind11) to avoid the build error, Python crashes when attempting to import the module. (Der Absturz geschieht, wie im Folgenden beschrieben, innerhalb des DLL-Aufrufs von PyModule_Create, mit der Ausgabemeldung Fatal Python error: PyThreadState_Get: no current thread (Schwerwiegender Python-Fehler: PyThreadState_Get: kein aktueller Thread.))(The crash happens within the DLL's call to PyModule_Create as described later, with the output message of Fatal Python error: PyThreadState_Get: no current thread.)

    Die /MDd-Option wird zum Erstellen der Python-Debugbinärdateien (z.B. python_d.exe) verwendet. Wenn Sie sie jedoch für eine Erweiterungs-DLL verwenden, wird weiterhin der Buildfehler mit Py_LIMITED_API verursacht.The /MDd option is used to build the Python debug binaries (such as python_d.exe), but selecting it for an extension DLL still causes the build error with Py_LIMITED_API.

  8. Klicken Sie mit der mit der rechten Maustaste auf das C++-Projekt, und wählen Sie Erstellen aus, um Ihre Konfigurationen (Debug und Release) zu testen.Right-click the C++ project and select Build to test your configurations (both Debug and Release). Die .pyd-Dateien sind im Projektmappenordner unter Debug und Release und nicht im C++-Projektordner selbst zu finden.The .pyd files are located in the solution folder under Debug and Release, not the C++ project folder itself.

  9. Fügen Sie der Datei module.cpp des C++-Projekts folgenden Code hinzu:Add the following code to the C++ project's module.cpp file:

    #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);
    }
    
  10. Erstellen Sie das C++-Projekt erneut, um zu bestätigen, dass Ihr Code korrekt ist.Build the C++ project again to confirm that your code is correct.

  11. Sofern Sie dies noch nicht getan haben, wiederholen Sie die oben genannten Schritte, um ein zweites Projekt mit dem Namen "superfastcode2" mit identischem Inhalt zu erstellen.If you haven't already done so, repeat the steps above to create a second project named "superfastcode2" with identical contents.

Konvertieren von C++-Projekten in Erweiterungen für PythonConvert the C++ projects to extensions for Python

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.To make the C++ DLL into an extension for Python, you first modify the exported methods to interact with Python types. Anschließend fügen Sie eine Funktion hinzu, die das Modul zusammen mit den Definitionen der Modulmethoden exportiert.You then add a function that exports the module, along with definitions of the module's methods.

In den folgenden Abschnitten wird erläutert, wie diese Schritte unter Verwendung von CPython-Erweiterungen und PyBind11 durchgeführt werden.The sections that follow explain how to perform these steps using both the CPython extensions and PyBind11.

CPython-ErweiterungenCPython extensions

Hintergrundinformationen zu dem, was in diesem Abschnitt für Python 3.x gezeigt wird, finden Sie im Python/C++-API-Referenzhandbuch und insbesondere in den Modulobjekten auf python.org. (Denken Sie daran, Ihre Python-Version oben rechts im Dropdownmenü auszuwählen, um die richtige Dokumentation anzuzeigen).For background on what's shown in this section for Python 3.x, refer to the Python/C API Reference Manual and especially Module Objects on python.org (remember to select your version of Python from the drop-down control on the upper right to view the correct documentation).

Beziehen Sie sich bei der Arbeit mit Python 2.7 stattdessen auf die Artikel Extending Python 2.7 with C or C++ (Erweitern von Python 2.7 mit C oder C++) und Porting Extension Modules to Python 3 (Portierung von Erweiterungsmodulen auf Python 3) (python.org).If you're working with Python 2.7, refer instead to Extending Python 2.7 with C or C++ and Porting Extension Modules to Python 3 (python.org).

  1. Fügen Sie am Anfang der module.cpp-Datei Python.h ein:At the top of module.cpp, include Python.h:

    #include <Python.h>
    
  2. Ändern Sie die tanh_impl-Methode, um Python-Typen (d.h. ein PyOjbect*-Objekt) zu akzeptieren und zurückzugeben:Modify the tanh_impl method to accept and return Python types (a PyOjbect*, that is):

    PyObject* tanh_impl(PyObject *, PyObject* o) {
        double x = PyFloat_AsDouble(o);
        double tanh_x = sinh_impl(x) / cosh_impl(x);
        return PyFloat_FromDouble(tanh_x);
    }
    
  3. Fügen Sie eine Struktur hinzu, die definiert, wie die tanh_impl-Funktion von C++ Python angezeigt wird:Add a structure that defines how the C++ tanh_impl function is presented to Python:

    static PyMethodDef superfastcode_methods[] = {
        // The first property is the name exposed to Python, fast_tanh, the second is the C++
        // function name that contains the implementation.
        { "fast_tanh", (PyCFunction)tanh_impl, METH_O, nullptr },
    
        // Terminate the array with an object containing nulls.
        { nullptr, nullptr, 0, nullptr }
    };
    
  4. Fügen Sie eine Struktur hinzu, die das Modul so definiert, wie Sie im Python-Code darauf verweisen möchten, insbesondere bei Verwendung der from...import-Anweisung.Add a structure that defines the module as you want to refer to it in your Python code, specifically when using the from...import statement. (Stellen Sie sicher, dass dies mit dem Wert in den Projekteigenschaften unter Konfigurationseigenschaften > Allgemein > Zielname übereinstimmt.) Im folgenden Beispiel bedeutet der Modulname „superfastcode“, dass Sie from superfastcode import fast_tanh in Python verwenden können, da fast_tanh in superfastcode_methods definiert ist.(Make this match the value in the project properties under Configuration Properties > General > Target Name.) In the following example, the "superfastcode" module name means you can use from superfastcode import fast_tanh in Python, because fast_tanh is defined within superfastcode_methods. (Dateinamen innerhalb des C++-Projekts wie module.cpp sind nicht von Bedeutung.)(Filenames internal to the C++ project, like module.cpp, are inconsequential.)

    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
    };
    
  5. Fügen Sie eine Methode hinzu, die Python aufruft, wenn das Modul geladen wird, die PyInit_<module-name> genannt werden muss, wobei <module-name> genau mit der Eigenschaft Allgemein > Zielname des C++-Projekts übereinstimmt (das bedeutet, dass sie mit dem Dateinamen von .pyd übereinstimmt, der vom Projekt erstellt wird).Add a method that Python calls when it loads the module, which must be named PyInit_<module-name>, where <module-name> exactly matches the C++ project's General > Target Name property (that is, it matches the filename of the .pyd built by the project).

    PyMODINIT_FUNC PyInit_superfastcode() {
        return PyModule_Create(&superfastcode_module);
    }
    
  6. Legen Sie die Zielkonfiguration auf Release fest, und erstellen Sie das C++-Projekt erneut, um Ihren Code zu überprüfen.Set the target configuration to Release and build the C++ project again to verify your code. Informationen zur Problembehandlung finden Sie im folgenden Abschnitt.If you encounter errors, see the Troubleshooting section below.

PyBind11PyBind11

Wenn Sie die Schritte im vorherigen Abschnitt abgeschlossen haben, haben Sie sicher bemerkt, dass Sie zum Erstellen der erforderlichen Modulstrukturen für den C++-Code viele Codebausteine verwendet haben.If you completed the steps in the previous section, you certainly noticed that you used lots of boilerplate code to create the necessary module structures for the C++ code. PyBind11 vereinfacht den Prozess mithilfe von Makros in einer C++-Headerdatei, sodass mit weniger Code das gleiche Ergebnis erzielt werden kann.PyBind11 simplifies the process through macros in a C++ header file that accomplish the same result with much less code. Hintergrundinformationen zum Inhalt dieses Abschnitts finden Sie unter PyBind11-Grundlagen (github.com).For background on what's shown in this section, see PyBind11 basics (github.com).

  1. Installieren Sie PyBind11 unter Verwendung von „pip“: pip install pybind11 oder py -m pip install pybind11.Install PyBind11 using pip: pip install pybind11 or py -m pip install pybind11.

  2. Fügen Sie am Anfang der module.cpp-Datei pybind11.h ein.At the top of module.cpp, include pybind11.h:

    #include <pybind11/pybind11.h>
    
  3. Verwenden Sie am Ende der module.cpp-Datei das Makro PYBIND11_MODULE, um den Einstiegspunkt zur C++-Funktion zu definieren.At the bottom of module.cpp, use the PYBIND11_MODULE macro to define the entrypoint to the C++ function:

    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. Legen Sie die Zielkonfiguration auf Release fest, und erstellen Sie das C++-Projekt, um Ihren Code zu überprüfen.Set the target configuration to Release and build the C++ project to verify your code. Besuchen Sie zur Problembehandlung den nächsten Abschnitt, wenn Fehler auftreten.If you encounter errors, see the next section on troubleshooting.

ProblembehandlungTroubleshooting

Beim C++-Modul treten möglicherweise aus den folgenden Gründen Fehler beim Kompilieren auf:The C++ module may fail to compile for the following reasons:

  • Python.h kann nicht gefunden werden (E1696: cannot open source file „Python.h“ (E1696: Die Quelldatei „Python.h“ kann nicht geöffnet werden) und oder C1083: cannot open include file: „Python.h“: No such file or directory (Includedatei kann nicht geöffnet werden: „Python.h“. Datei oder Verzeichnis existiert nicht.) ): Überprüfen Sie, ob der Pfad in C/C++ > Allgemein > Zusätzliche Includeverzeichnisse in den Projekteigenschaften auf den Include-Ordner Ihrer Python-Installation zeigt.Unable to locate Python.h (E1696: cannot open source file "Python.h" and/or C1083: Cannot open include file: "Python.h": No such file or directory): verify that the path in C/C++ > General > Additional Include Directories in the project properties points to your Python installation's include folder. Siehe Schritt 6 unter Erstellen des wesentlichen C++-Projekts.See step 6 under Create the core C++ project.

  • Die Python-Bibliotheken konnten nicht gefunden werden: Überprüfen Sie, ob der Pfad in Linker > Allgemein > Zusätzliche Bibliotheksverzeichnisse in den Projekteigenschaften auf den libs-Ordner Ihrer Python-Installation zeigt.Unable to locate Python libraries: verify that the path in Linker > General > Additional Library Directories in the project properties points to your Python installation's libs folder. Siehe Schritt 6 unter Erstellen des wesentlichen C++-Projekts.See step 6 under Create the core C++ project.

  • Linker-Fehler im Zusammenhang mit der Zielarchitektur: Ändern Sie die Zielarchitektur des C++-Projekts so, dass sie mit der Ihrer Python-Installation übereinstimmt.Linker errors related to target architecture: change the C++ target's project architecture to match that of your Python installation. Wenn Sie z.B. mit dem C++-Projekt auf x64 abzielen, aber Ihre Python-Installation ein x86-System ist, ändern Sie das C++-Projekt auf x86 ab.For example, if you're targeting x64 with the C++ project but your Python installation is x86, change the C++ project to target x86.

Testen des Codes und Vergleichen der ErgebnisseTest the code and compare the results

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.Now that you have the DLLs structured as Python extensions, you can refer to them from the Python project, import the modules, and use their methods.

Verfügbarmachen der DLL für PythonMake the DLL available to Python

Es gibt zwei Methoden, um die DLL Python zur Verfügung zu stellen.There are two ways to make the DLL available to Python.

Die erste Methode funktioniert, wenn das Python-Projekt und das C++-Projekt sich in derselben Projektmappe befinden.The first method works if the Python project and the C++ project are in the same solution. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf den Knoten Verweise Ihres Python-Projekts, und klicken Sie dann auf Verweis hinzufügen.Go to Solution Explorer, right-click the References node in your Python project, and then select Add Reference. Klicken Sie in dem angezeigten Dialogfeld auf die Registerkarte Projekte, wählen Sie anschließend die Projekte superfastcode und superfastcode2 aus, und klicken Sie dann auf OK.In the dialog that appears, select the Projects tab, select both the superfastcode and superfastcode2 projects, and then select OK.

Hinzufügen eines Verweises auf das superfastcode-Projekt

Durch die alternative Methode, die in den folgenden Schritten beschrieben wird, wird das Modul in der globalen Python-Umgebung installiert. Dadurch wird es ebenfalls für andere Python-Projekte zur Verfügung gestellt.The alternate method, described in the following steps, installs the module in the global Python environment, making it available to other Python projects as well. (In der Regel erfordert dies, dass Sie die IntelliSense-Vervollständigungsdatenbank für diese Umgebung in Visual Studio 2017 Version 15.5 und früher aktualisieren.(Doing so typically requires that you refresh the IntelliSense completion database for that environment in Visual Studio 2017 version 15.5 and earlier. Eine Aktualisierung ist ebenfalls erforderlich, wenn das Modul aus der Umgebung entfernt wird.)Refreshing is also necessary when removing the module from the environment.)

  1. Wenn Sie Visual Studio 2017 oder höher verwenden, führen Sie den Visual Studio-Installer aus, wählen Sie Ändern und anschließend Einzelne Komponenten > Compiler, Buildtools und Runtimes > Visual C++ 2015.3 v140-Toolset aus.If you're using Visual Studio 2017 or later, run the Visual Studio installer, select Modify, select Individual Components > Compilers, build tools, and runtimes > Visual C++ 2015.3 v140 toolset. Dieser Schritt ist notwendig, da Python (für Windows) selbst mit Visual Studio 2015 (Version 14.0) erstellt wurde und erwartet, dass diese Tools beim Erstellen einer Erweiterung über die hier beschriebene Methode verfügbar ist.This step is necessary because Python (for Windows) is itself built with Visual Studio 2015 (version 14.0) and expects that those tools are available when building an extension through the method described here. (Beachten Sie, dass Sie möglicherweise eine 32-Bit-Version von Python installieren müssen, um die DLL auf Win32 statt x64 auszurichten.)(Note that you may need to install a 32-bit version of Python and target the DLL to Win32 and not x64.)

  2. Erstellen Sie im C++-Projekt eine Datei mit dem Namen setup.py, indem Sie mit der rechten Maustaste auf Projekt klicken und anschließend auf Hinzufügen > Neues Element.Create a file named setup.py in the C++ project by right-clicking the project and selecting Add > New Item. Wählen Sie dann C++-Datei (.cpp) aus, benennen Sie die Datei setup.py, und wählen Sie anschließend auf OK aus (wenn Sie die Datei mit der .py-Erweiterung benennen, erkennt Visual Studio sie als Python an, obwohl die C++-Dateivorlage verwendet wird).Then select C++ File (.cpp), name the file setup.py, and select OK (naming the file with the .py extension makes Visual Studio recognize it as Python despite using the C++ file template). Wenn die Datei im Editor angezeigt wird, fügen Sie den nachstehenden für die Erweiterungsmethode geeigneten Code ein:When the file appears in the editor, paste the following code into it as appropriate to the extension method:

    CPython-Erweiterungen (superfastcode-Projekt):CPython extensions (superfastcode project):

    from distutils.core import setup, Extension, DEBUG
    
    sfc_module = Extension('superfastcode', sources = ['module.cpp'])
    
    setup(name = 'superfastcode', version = '1.0',
        description = 'Python Package with superfastcode C++ extension',
        ext_modules = [sfc_module]
        )
    

    Weitere Informationen zur Dokumentation für dieses Skript finden Sie unter Building C and C++ Extensions (Erstellen von C- und C++-Erweiterungen) (python.org).See Building C and C++ extensions (python.org) for documentation on this script.

    PyBind11 (superfastcode2-Projekt):PyBind11 (superfastcode2 project):

    import os, sys
    
    from distutils.core import setup, Extension
    from distutils import sysconfig
    
    cpp_args = ['-std=c++11', '-stdlib=libc++', '-mmacosx-version-min=10.7']
    
    sfc_module = Extension(
        'superfastcode2', sources = ['module.cpp'],
        include_dirs=['pybind11/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],
    )
    
  3. Der setup.py-Code weist Python an, die Erweiterung mithilfe des C++-Toolsets von Visual Studio 2015 zu erstellen, wenn Sie auf der Befehlszeile verwendet wird.The setup.py code instructs Python to build the extension using the Visual Studio 2015 C++ toolset when used from the command line. Öffnen Sie eine Eingabeaufforderung mit erhöhten Rechten, navigieren Sie zum Ordner mit dem C++-Projekt (d.h. der Ordner, der setup.py enthält), und geben Sie den folgenden Befehl ein:Open an elevated command prompt, navigate to the folder containing the C++ project (that is, the folder that contains setup.py), and enter the following command:

    pip install .
    

    oder:or:

    py -m pip install .
    

Aufrufen der DLL von PythonCall the DLL from Python

Nachdem Sie die DLL wie im vorherigen Abschnitt beschrieben für Python verfügbar gemacht haben, können Sie nun die Funktionen superfastcode.fast_tanh und superfastcode2.fast_tanh2 aus dem Python-Code aufrufen und deren Leistung mit der Python-Implementierung vergleichen.After you've made the DLL available to Python as described in the previous section, you can now call the superfastcode.fast_tanh and superfastcode2.fast_tanh2 functions from Python code and compare their performance to the Python implementation:

  1. Fügen Sie die folgenden Zeilen zu der .py-Datei hinzu, um aus den DLLs exportierte Methoden aufzurufen und deren Ausgaben anzuzeigen:Add the following lines in your .py file to call methods exported from the DLLs and display their outputs:

    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)')
    
  2. Wenn Sie das Python-Programm ausführen (Debuggen > Starten ohne Debuggen oder STRG+F5), können Sie beobachten, dass die C++-Routinen ca. fünf- bis zwanzigmal schneller als die Python-Implementierung ausgeführt wird.Run the Python program (Debug > Start without Debugging or Ctrl+F5) and observe that the C++ routines run approximately five to twenty times faster than the Python implementation. Eine typische Ausgabe sieht wie folgt aus:Typical output appears as follows:

    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
    

    Wenn der Befehl Ohne Debuggen starten deaktiviert ist, klicken Sie mit der rechten Maustaste im Projektmappen-Explorer auf das Python-Projekt, und wählen Sie Set as Startup Project (Als Startprojekt festlegen) aus.If the Start Without Debugging command is disabled, right-click the Python project in Solution Explorer and select Set as Startup Project.

  3. Versuchen Sie die COUNT-Variable zu vergrößern, sodass die Unterschiede deutlicher werden.Try increasing the COUNT variable so that the differences are more pronounced. Beachten Sie, dass ein Debugbuild des C++-Moduls zudem langsamer als ein Releasebuild ausgeführt wird, da der Debugbuild weniger optimiert ist und verschiedene Fehlerprüfungen enthält.A Debug build of the C++ module also runs slower than a Release build because the Debug build is less optimized and contains various error checks. Sie können zum Vergleich zwischen diesen Konfigurationen wechseln.Feel free to switch between those configurations for comparison.

Hinweis

In der Ausgabe können Sie sehen, dass die PyBind11-Erweiterung zwar nicht so schnell wie die CPython-Erweiterung, aber dennoch deutlich schneller als die normale Python-Implementierung ist.In the output, you can see that the PyBind11 extension isn't as fast as the CPython extension, though it's still significantly faster than the straight Python implementation. Der Unterschied ist auf geringen aufrufspezifischen Aufwand zurückzuführen, der von PyBind11 verursacht wird, um die C++-Schnittstelle erheblich zu vereinfachen.The difference is due to a small amount of per-call overhead that PyBind11 introduces in order to make its C++ interface dramatically simpler. Dieser aufrufspezifische Unterschied ist tatsächlich ziemlich gering: Da der Testcode die Erweiterungsfunktionen 500.000 Mal aufruft, erhöhen die hier angezeigten Ergebnisse diesen Aufwand.This per-call difference is actually quite negligible: because the test code calls the extension functions 500,000 times, the results you see here greatly amplify that overhead! In der Regel leistet eine C++-Funktion mehr Arbeit als die hier verwendeten normalen fast_tanh[2]-Methoden, bei denen der Aufwand nicht von Bedeutung ist.Typically, a C++ function does much more work than the trivial fast_tanh[2] methods used here, in which case the overhead is unimportant. Dennoch führt die Verwendung des CPython-Ansatzes beim Implementieren von Methoden, die möglicherweise tausendfach pro Minute aufgerufen werden, zu einer besseren Leistung als die Verwendung des PyBind11-Ansatzes.However, if you're implementing methods that might be called thousands of times per second, using the CPython approach can result in better performance than PyBind11.

Debuggen des C++-CodesDebug the C++ code

Visual Studio unterstützt das gemeinsame Debuggen von Python- und C++-Code.Visual Studio supports debugging Python and C++ code together. Dieser Abschnitt veranschaulicht den Prozess mithilfe des Projekts superfastcode. Die Schritte für das Projekt superfastcode2 sind identisch.This section walks through the process using the superfastcode project; the steps are the same for the superfastcode2 project.

  1. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Python-Projekt, wählen Sie Eigenschaften und die Registerkarte Debuggen aus, und wählen Sie anschließend die Option Debuggen > Debuggen von nativem Code aktivieren aus.Right-click the Python project in Solution Explorer, select Properties, select the Debug tab, and then select the Debug > Enable native code debugging option.

    Tipp

    Wenn Sie das Debuggen von nativem Code aktivieren, kann das Python-Ausgabefenster möglicherweise sofort verschwinden, wenn das Programm ohne die übliche Pause Drücken Sie eine beliebige Taste, um fortzufahren... abgeschlossen wurde.When you enable native code debugging, the Python output window may disappear immediately when the program has completed without giving you the usual Press any key to continue pause. Um eine Pause zu erzwingen, fügen Sie die -i-Option dem Feld Ausführen > Interpreterargumente auf der Registerkarte Debuggen hinzu, wenn Sie das Debuggen von nativem Code aktivieren.To force a pause, add the -i option to the Run > Interpreter Arguments field on the Debug tab when you enable native code debugging. Dieses Argument den Python-Interpreter in den interaktiven Modus versetzt, nach dem Code abgeschlossen ist, die zu diesem Zeitpunkt wartet er Sie drücken STRG+Z > EINGABETASTE zu beenden.This argument puts the Python interpreter into interactive mode after the code finishes, at which point it waits for you to press Ctrl+Z > Enter to exit. (Wenn es Ihnen nichts ausmacht, Ihren Python-Code zu ändern, können Sie auch import os- und os.system("pause")-Anweisungen an das Ende Ihres Programms hinzufügen.(Alternately, if you don't mind modifying your Python code, you can add import os and os.system("pause") statements at the end of your program. Durch diesen Code wird die ursprüngliche Aufforderung zur Pause dupliziert.)This code duplicates the original pause prompt.)

  2. Klicken Sie auf Datei > Speichern, um die Änderungen an den Eigenschaften zu speichern.Select File > Save to save the property changes.

  3. Legen Sie die Buildkonfiguration in der Visual Studio-Symbolleiste Debuggen fest.Set the build configuration to Debug in the Visual Studio toolbar.

    Festlegen der Buildkonfiguration auf „Debuggen“

  4. Da es in der Regel länger dauert, Code im Debugger auszuführen, sollten Sie die COUNT-Variable in der .py-Datei auf einen fünfmal niedrigeren Wert (z.B. von 500000 auf 100000) festlegen.Because code generally takes longer to run in the debugger, you may want to change the COUNT variable in your .py file to a value that's about five times smaller (for example, change it from 500000 to 100000).

  5. Legen Sie einen Haltepunkt auf der ersten Zeile der tanh_impl-Methode in Ihrem C++-Code fest, und starten Sie anschließend den Debugger (F5 oder Debuggen > Debuggen starten).In your C++ code, set a breakpoint on the first line of the tanh_impl method, then start the debugger (F5 or Debug > Start Debugging). Der Debugger wird angehalten, wenn dieser Code aufgerufen wird.The debugger stops when that code is called. Wenn der Haltepunkt nicht getroffen wird, überprüfen Sie, ob die Konfiguration auf Debuggen festgelegt ist und Sie das Projekt gespeichert haben. (Dies geschieht nicht automatisch beim Starten des Debuggers).If the breakpoint is not hit, check that the configuration is set to Debug and that you've saved the project (which does not happen automatically when starting the debugger).

    Anhalten an einem Haltepunkt im C++-Code

  6. An diesem Punkt können Sie den C++-Code schrittweise durchgehen, Variablen prüfen, etc.At this point you can step through the C++ code, examine variables, and so on. Diese Funktionen werden detailliert unter Gemeinsames Debuggen von Python und C++ beschrieben.These features are detailed in Debug C++ and Python together.

Alternative AnsätzeAlternative approaches

Es gibt wie in der folgenden Tabelle beschrieben verschiedene Methoden zum Erstellen von Python-Erweiterungen.There are a variety of means to create Python extensions as described in the following table. Die ersten beiden Einträge für CPython und PyBind11 enthalten den bereits in diesem Artikel beschriebenen Inhalt.The first two entries for CPython and PyBind11 are what has been discussed in this article already.

AnsatzApproach GenerationVintage RechtsvertreterRepresentative user(s) Pro(s)Pro(s) Kon(s)Con(s)
C/C++-Erweiterungsmodule für CPythonC/C++ extension modules for CPython 19911991 StandardbibliothekStandard Library Ausführliche Dokumentation und TutorialsExtensive documentation and tutorials. Vollständige Kontrolle.Total control. Kompilierung, Portabilität, VerweisverwaltungCompilation, portability, reference management. Hohe C-Kenntnisse.High C knowledge.
PyBind11 (empfohlen für C++)PyBind11 (Recommended for C++) 20152015 Einfache, reine Headerbibliothek zur Erstellung von Python-Bindungen von bestehendem C++-CodeLightweight, header-only library for creating Python bindings of existing C++ code. Wenige AbhängigkeitenFew dependencies. PyPy-KompatibilitätPyPy compatibility. Neuer, weniger ausgefeiltNewer, less mature. Vermehrter Einsatz von C++11-FeaturesHeavy use of C++11 features. Kurze Liste unterstützter Compiler (Visual Studio ist enthalten)Short list of supported compilers (Visual Studio is included).
Cython (empfohlen für C)Cython (Recommended for C) 20072007 gevent, kivygevent, kivy Python-ähnlich.Python-like. Sehr fortgeschrittenen.Highly mature. Hohe Leistung.High performance. Kompilierung, neue Syntax, neue Toolkette.Compilation, new syntax, new toolchain.
Boost.PythonBoost.Python 20022002 Funktioniert mit nur nahezu jedem C++-Compiler.Works with just about every C++ compiler. Große und komplexe Sammlung von Bibliotheken; enthält viele Problemumgehungen für alte Compiler.Large and complex suite of libraries; contains many workarounds for old compilers.
ctypesctypes 20032003 oscryptooscrypto Keine Kompilierung, breite VerfügbarkeitNo compilation, wide availability. Unpraktisches und fehleranfälliges Zugreifen und Mutieren von C-StrukturenAccessing and mutating C structures cumbersome and error prone.
SWIGSWIG 19961996 crfsuitecrfsuite Generieren von Bindungen gleichzeitig für mehrere SprachenGenerate bindings for many languages at once. Übermäßiger Mehraufwand, wenn Python das einzige Ziel darstelltExcessive overhead if Python is the only target.
cfficffi 20132013 cryptography, pypycryptography, pypy Einfache Integration, PyPy-KompatibilitätEase of integration, PyPy compatibility. Neuer, weniger ausgefeiltNewer, less mature.
cppyycppyy 20172017 Ähnlich wie cffi mit C++.Similar to cffi using C++. Neuer, kann möglicherweise zu Problemen mit Visual Studio 2017 führen.Newer, may have some issues with VS 2017.

Siehe auchSee also

Das abgeschlossene Beispiel aus dieser exemplarischen Vorgehensweise finden Sie unter python-samples-vs-cpp-extension (GitHub).The completed sample from this walkthrough can be found on python-samples-vs-cpp-extension (GitHub).