Creación de una extensión de C++ para PythonCreate a C++ extension for Python

Los módulos escritos en C++ (o C) se suelen usar para ampliar las capacidades de un intérprete de Python y para permitir el acceso a funciones de bajo nivel del sistema operativo.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. Hay tres tipos principales de módulos:There are three primary types of modules:

  • Módulos de acelerador: dado que Python es un lenguaje interpretado, algunas partes del código pueden escribirse en C++ para un mayor rendimiento.Accelerator modules: because Python is an interpreted language, certain pieces of code can be written in C++ for higher performance.
  • Módulos de contenedor: exponen las interfaces de C o C++ existentes a código de Python o exponen una API más propia de Python que se pueda usar fácilmente con este código.Wrapper modules: expose existing C/C++ interfaces to Python code or expose a more "Pythonic" API that's easy to use from Python.
  • Módulos de acceso al sistema de bajo nivel: creados para tener acceso a características de bajo nivel del tiempo de ejecución de CPython, el sistema operativo o el hardware subyacente.Low-level system access modules: created to access lower-level features of the CPython runtime, the operating system, or the underlying hardware.

Este artículo le guía por la compilación de un módulo de extensión de C++ para CPython que calcula la tangente hiperbólica y realiza una llamada desde el código de Python.This article walks through building a C++ extension module for CPython that computes a hyperbolic tangent and calls it from Python code. Primero, se implementa la rutina en Python para demostrar la mejora relativa del rendimiento de la implementación de la misma rutina en C++.The routine is implemented first in Python to demonstrate the relative performance gain of implementing the same routine in C++.

En este artículo también se muestran dos formas para que C++ esté disponible para Python:This article also demonstrates two ways to make the C++ available to Python:

Al final de este artículo, en Enfoques alternativos, se describe una comparación entre estos y otros medios.A comparison between these and other means is found under alternative approaches at the end of this article.

El ejemplo completo de este tutorial se puede encontrar en python-samples-vs-cpp-extension (GitHub).The completed sample from this walkthrough can be found on python-samples-vs-cpp-extension (GitHub).

Requisitos previosPrerequisites

  • Visual Studio 2017 con las cargas de trabajo Desarrollo para el escritorio con C++ y Desarrollo de Python instaladas con opciones predeterminadas.Visual Studio 2017 with both the Desktop Development with C++ and Python Development workloads installed with default options.

  • En la carga de trabajo Desarrollo de Python, seleccione también el cuadro de la derecha de Herramientas de desarrollo nativo de Python.In the Python Development workload, also select the box on the right for Python native development tools. Esta opción establece la mayor parte de la configuración descrita en este artículo.This option sets up most of the configuration described in this article. (Esta opción también incluye la carga de trabajo de C++ automáticamente).(This option also includes the C++ workload automatically.)

    Selección de la opción Herramientas de desarrollo nativo Python

    Tip

    La instalación de la carga de trabajo Aplicaciones de ciencia de datos y de análisis también incluye Python y la opción Herramientas de desarrollo nativo Python de forma predeterminada.Installing the Data science and analytical applications workload also includes Python and the Python native development tools option by default.

Para obtener más información, vea Instalación de la compatibilidad con Python en Visual Studio, que incluye el uso de otras versiones de Visual Studio.For more information, see Install Python support for Visual Studio, including using other versions of Visual Studio. Si instala Python por separado, asegúrese de seleccionar Download debugging symbols (Descargar símbolos de depuración) y Download debug binaries (Descargar archivos binarios de depuración) en Opciones avanzadas en el programa de instalación.If you install Python separately, be sure to select Download debugging symbols and Download debug binaries under Advanced Options in the installer. Esta opción le asegura que tendrá disponibles las bibliotecas de depuración necesarias si decide realizar una compilación de depuración.This option ensures that you have the necessary debug libraries available if you choose to do a debug build.

Crear la aplicación de PythonCreate the Python application

  1. Cree un proyecto de Python en Visual Studio. Para ello, seleccione Archivo > Nuevo > Proyecto.Create a new Python project in Visual Studio by selecting File > New > Project. Busque “Python”, seleccione la plantilla Aplicación de Python, dele un nombre, asígnele una ubicación y seleccione Aceptar.Search for "Python", select the Python Application template, give it a suitable name and location, and select OK.

  2. Para trabajar con C++ es necesario usar un intérprete de Python de 32 bits (se recomienda Python 3.6 o versiones posteriores).Working with C++ requires that you use a 32-bit Python interpreter (Python 3.6 or above recommended). En la ventana Explorador de soluciones de Visual Studio, expanda el nodo del proyecto y luego expanda el nodo Entornos de Python.In the Solution Explorer window of Visual Studio, expand the project node, then expand the Python Environments node. Si no ve un entorno de 32 bits como el valor predeterminado (ya sea en negrita o con la etiqueta valor predeterminado global), siga las instrucciones que figuran en Cómo asignar el entorno de Python que se usa en un proyecto.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. Si no tiene instalado un intérprete de 32 bits, consulte Instalación de intérpretes de Python.If you don't have a 32-bit interpreter installed, see Install Python interpreters.

  3. En el archivo .py del proyecto, pegue el código siguiente, que realiza una prueba comparativa del cálculo de una tangente hiperbólica (implementada sin usar la biblioteca matemática para facilitar la comparación).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). No dude en escribir el código manualmente para experimentar algunas de las características de edición de Python.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. Ejecute el programa mediante Depurar > Iniciar sin depurar (Ctrl+F5) para ver los resultados.Run the program using Debug > Start without Debugging (Ctrl+F5) to see the results. Puede ajustar la variable COUNT para cambiar el tiempo que tarda en ejecutarse la prueba comparativa.You can adjust the COUNT variable to change how long the benchmark takes to run. Para los fines de este tutorial, establezca el número para que la prueba comparativa tarde aproximadamente dos segundos.For the purposes of this walkthrough, set the count so that the benchmark take around two seconds.

Tip

Al ejecutar pruebas comparativas, use siempre Depurar > Iniciar sin depurar para evitar la sobrecarga producida cuando se ejecuta código dentro del depurador de Visual Studio.When running benchmarks, always use Debug > Start without Debugging to avoid the overhead incurred when running code within the Visual Studio debugger.

Crear los proyectos principales de C++Create the core C++ projects

Siga las instrucciones de esta sección para crear dos proyectos de C++ idénticos denominados "superfastcode" y "superfastcode2".Follow the instructions in this section to create two identical C++ projects named "superfastcode" and "superfastcode2". Más adelante usará distintos medios en cada proyecto para exponer el código de C++ para Python.Later you'll use different means in each project to expose the C++ code to Python.

  1. Asegúrese de que la variable de entorno PYTHONHOME esté establecida en el intérprete de Python que quiere usar.Make sure the PYTHONHOME environment variable is set to the Python interpreter you want to use. Los proyectos de C++ en Visual Studio se basan en esta variable para ubicar archivos como python.h, que se usan al crear una extensión de Python.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. Haga clic con el botón derecho en la solución en el Explorador de soluciones y seleccione Agregar > Nuevo proyecto.Right-click the solution in Solution Explorer and select Add > New Project. Una solución de Visual Studio puede contener proyectos de Python y C++ juntos (que es una de las ventajas de utilizar Visual Studio para Python).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. Busque "C++", seleccione Proyecto vacío, especifique el nombre "superfastcode" ("superfastcode2" para el segundo proyecto) y seleccione Aceptar.Search on "C++", select Empty project, specify the name "superfastcode" ("superfastcode2" for the second project), and select OK.

    Tip

    Con las herramientas de desarrollo nativo de Python instaladas en Visual Studio 2017, puede comenzar con la plantilla Módulo de extensión de Python, que incluye gran parte de lo que se describe a continuación.With the Python native development tools installed in Visual Studio 2017, you can start with the Python Extension Module template instead, which has much of what's described below already in place. En este tutorial partiremos de un proyecto vacío para mostrar la compilación del módulo de extensión paso a paso.For this walkthrough, though, starting with an empty project demonstrates building the extension module step by step. Una vez que comprenda el proceso, la plantilla le permite ahorrar tiempo al escribir sus propias extensiones.Once you understand the process, the template saves you time when writing your own extensions.

  4. Cree un archivo de C++ en el nuevo proyecto. Para ello, haga clic con el botón derecho en el nodo Archivos de código fuente, seleccione Agregar > Nuevo elemento, seleccione Archivo de C++, asígnele el nombre module.cpp y seleccione Aceptar.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.

    Important

    Para activar las páginas de propiedades de C++ en los pasos siguientes, es necesario un archivo con la extensión .cpp.A file with the .cpp extension is necessary to turn on the C++ property pages in the steps that follow.

  5. Haga clic con el botón derecho en el proyecto de C++ en el Explorador de soluciones y seleccione Propiedades.Right-click the C++ project in Solution Explorer, select Properties.

  6. En la parte superior del cuadro de diálogo Páginas de propiedades que aparece, establezca Configuración como Todas las configuraciones y Plataforma como Win32.At the top of the Property Pages dialog that appears, set Configuration to All Configurations and Platform to Win32.

  7. Establezca las propiedades específicas tal y como se describe en la tabla siguiente y, luego, seleccione Aceptar.Set the specific properties as described in the following table, then select OK.

    TabTab Propiedad.Property ValorValue
    GeneralGeneral General > Nombre de destinoGeneral > Target Name Especifique el nombre del módulo tal y como quiera referirse a él desde Python en las instrucciones from...import.Specify the name of the module as you want to refer to it from Python in from...import statements. Use este mismo nombre en C++ al definir el módulo para Python.You use this same name in the C++ when defining the module for Python. Si quiere utilizar el nombre del proyecto como nombre del módulo, deje el valor predeterminado de $(ProjectName).If you want to use the name of the project as the module name, leave the default value of $(ProjectName).
    General > Extensión de destinoGeneral > Target Extension .pyd.pyd
    Valores predeterminados del proyecto > Tipo de configuraciónProject Defaults > Configuration Type Biblioteca dinámica (.dll)Dynamic Library (.dll)
    C/C++ > GeneralC/C++ > General Directorios de inclusión adicionalesAdditional Include Directories Agregue la carpeta include de Python según sea necesario para la instalación; por ejemplo, c:\Python36\include.Add the Python include folder as appropriate for your installation, for example, c:\Python36\include.
    C/C++ > PreprocesadorC/C++ > Preprocessor Definiciones de preprocesadorPreprocessor Definitions Agregue Py_LIMITED_API; al principio de la cadena (incluido el punto y coma).Add Py_LIMITED_API; to the beginning of the string (including the semicolon). Esta definición restringe algunas de las funciones a las que se puede llamar desde Python y hace que el código pueda moverse con mayor facilidad entre versiones diferentes de Python.This definition restricts some of the functions you can call from Python and makes the code more portable between different versions of Python.
    C/C++ > Generación de códigoC/C++ > Code Generation Biblioteca en tiempo de ejecuciónRuntime Library DLL multiproceso (/MD) (vea la advertencia siguiente)Multi-threaded DLL (/MD) (see Warning below)
    Vinculador > GeneralLinker > General Directorios de bibliotecas adicionalesAdditional Library Directories Agregue la carpeta libs de Python que contiene archivos .lib según sea necesario para la instalación, por ejemplo, c:\Python36\libs.Add the Python libs folder containing .lib files as appropriate for your installation, for example, c:\Python36\libs. (Asegúrese de apuntar a la carpeta libs que contiene archivos .lib, y no a la carpeta Lib que contiene archivos .py).(Be sure to point to the libs folder that contains .lib files, and not the Lib folder that contains .py files.)

    Tip

    Si no ve la pestaña C/C++ en las propiedades del proyecto, esto se debe a que este no contiene archivos identificados como archivos de código fuente de C/C++.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. Esta condición puede producirse si crea un archivo de código fuente sin una extensión .c o .cpp.This condition can occur if you create a source file without a .c or .cpp extension. Por ejemplo, si ha escrito accidentalmente module.coo en lugar de module.cpp en el anterior cuadro de diálogo Nuevo elemento, Visual Studio creará el archivo pero no establecerá el tipo de archivo en "Código C/C++", que es lo que activa la pestaña de propiedades de C/C++. Este error de identificación se mantiene incluso si cambia el nombre del archivo con la extensión .cpp.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. Para establecer el tipo de archivo correctamente, haga clic con el botón derecho en el archivo en el Explorador de soluciones, seleccione Propiedades y establezca Tipo de archivo en Código C/C++.To set the file type properly, right-click the file in Solution Explorer, select Properties, then set File Type to C/C++ Code.

    Warning

    Establezca siempre la opción C/C ++ > Generación de código > Biblioteca en tiempo de ejecución como DLL multiproceso (/MD), incluso para una configuración de depuración, ya que esta es la configuración con la que se compilan los archivos binarios de Python.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. Si, por accidente, establece la opción DLL de depuración multiproceso (/MDd), al compilar una configuración de depuración, se producirá el error C1189: Py_LIMITED_API no es compatible con Py_DEBUG, Py_TRACE_REFS ni Py_REF_DEBUG.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. Además, si quita Py_LIMITED_API para evitar el error de compilación, Python se bloqueará al intentar importar el módulo.Furthermore, if you remove Py_LIMITED_API to avoid the build error, Python crashes when attempting to import the module. (El bloqueo se produce en la llamada del archivo DLL a PyModule_Create, como se describe más adelante, con el mensaje de salida Fatal Python error: PyThreadState_Get: no current thread [Error irrecuperable Python: PyThreadState_Get: ningún subproceso actual]).(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.)

    La opción /MDd se usa para compilar los binarios de depuración de Python (como python_d.exe), pero, si se selecciona para un archivo DLL de extensión, también se producirá el error de compilación con Py_LIMITED_API.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. Haga clic con el botón derecho en el proyecto de C++ y seleccione Compilar para probar sus configuraciones (de Depuración y de Lanzamiento).Right-click the C++ project and select Build to test your configurations (both Debug and Release). Los archivos .pyd están en la carpeta solución en Depuración y Lanzamiento, no en la carpeta del proyecto de C++.The .pyd files are located in the solution folder under Debug and Release, not the C++ project folder itself.

  9. Agregue el código siguiente al archivo module.cpp del proyecto de C++: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. Compile el proyecto de C++ para confirmar que el código es correcto.Build the C++ project again to confirm that your code is correct.

  11. Si aún no lo ha hecho, repita los pasos anteriores para crear un segundo proyecto denominado "superfastcode2" con contenido idéntico.If you haven't already done so, repeat the steps above to create a second project named "superfastcode2" with identical contents.

Convertir los proyectos de C++ en extensiones para PythonConvert the C++ projects to extensions for Python

Para convertir el DLL de C++ en una extensión para Python, debe modificar primero los métodos exportados de modo que interactúen con tipos de Python.To make the C++ DLL into an extension for Python, you first modify the exported methods to interact with Python types. Después, debe agregar una función que exporte el módulo, junto con las definiciones de los métodos del módulo.You then add a function that exports the module, along with definitions of the module's methods.

En las secciones siguientes se explica cómo realizar estos pasos con las extensiones de CPython y PyBind11.The sections that follow explain how to perform these steps using both the CPython extensions and PyBind11.

Extensiones de CPythonCPython extensions

Para obtener información sobre lo que se muestra en esta sección acerca de Python 3.x, consulte el Manual de referencia de la API de Python/C y especialmente Objetos de módulo en python.org (recuerde que debe seleccionar la versión de Python en el control desplegable de la esquina superior derecha para ver la documentación correcta).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).

Si está trabajando con Python 2.7, consulte en su lugar Extending Python 2.7 with C or C++ (Extensión de Python 2.7 con C o C++) y Porting Extension Modules to Python 3 (Migración de módulos de extensión a 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. En la parte superior de module.cpp, incluya Python.h:At the top of module.cpp, include Python.h:

    #include <Python.h>
    
  2. Modifique el método tanh_impl para aceptar y devolver tipos de Python (PyOjbect*).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. Agregue una estructura que defina la manera en que la función tanh_impl de C++ se muestra en Python: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. Agregue una estructura que defina el módulo como quiere que se haga referencia a él en el código de Python, concretamente cuando se usa la instrucción from...import.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. (Hágalo coincidir con el valor de las propiedades del proyecto en Propiedades de configuración > General > Nombre de destino). En el ejemplo siguiente, el nombre de módulo "superfastcode" significa que puede usar from superfastcode import fast_tanh en Python, porque fast_tanh se define en superfastcode_methods.(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. (Los nombres de archivo internos del proyecto de C++, como module.cpp, no tienen importancia).(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. Agregue un método al que Python llame cuando cargue el módulo, con el nombre PyInit_<module-name>, donde <module_name> debe coincidir exactamente con la propiedad General > Nombre de destino del proyecto de C++ (es decir, coincide con el nombre del archivo de .pyd compilado por el proyecto).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. Establezca la configuración de destino en Lanzamiento y compile el proyecto de C++ de nuevo para comprobar el código.Set the target configuration to Release and build the C++ project again to verify your code. Si se producen errores, vea la sección Solución de problemas a continuación.If you encounter errors, see the Troubleshooting section below.

PyBind11PyBind11

Si ha completado los pasos descritos en la sección anterior, sin duda ha observado que usa una gran cantidad de código reutilizable para crear las estructuras de módulo necesarias para el código de C++.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 simplifica el proceso a través de macros en un archivo de encabezado de C++ que consigue el mismo resultado con mucho menos código.PyBind11 simplifies the process through macros in a C++ header file that accomplish the same result with much less code. Para obtener información sobre lo que se muestra en esta sección, consulte PyBind11 basics (Fundamentos de PyBind11, en github.com).For background on what's shown in this section, see PyBind11 basics (github.com).

  1. Instale PyBind11 mediante pip: pip install pybind11 o py -m pip install pybind11.Install PyBind11 using pip: pip install pybind11 or py -m pip install pybind11.

  2. En la parte superior de module.cpp, incluya pybind11.h:At the top of module.cpp, include pybind11.h:

    #include <pybind11/pybind11.h>
    
  3. En la parte inferior de module.cpp, use la macro PYBIND11_MODULE para definir el punto de entrada a la función de C++: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. Establezca la configuración de destino en Lanzamiento y compile el proyecto de C++ para comprobar el código.Set the target configuration to Release and build the C++ project to verify your code. Si se producen errores, vea la sección Solución de problemas a continuación.If you encounter errors, see the next section on troubleshooting.

Solución de problemasTroubleshooting

El módulo de C++ puede producir un error al compilar por las razones siguientes:The C++ module may fail to compile for the following reasons:

  • No se puede localizar Python.h (E1696: no se puede abrir el archivo de origen "Python.h" o C1083: no se puede abrir el archivo de inclusión "Python.h": no existe tal archivo o directorio): verifique que la ruta de acceso en C/C ++ > General > Directorios de inclusión adicionales de las propiedades del proyecto apunta a la carpeta include de la instalación de Python.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. Consulte el paso 6 en Crear el proyecto de C++ principal.See step 6 under Create the core C++ project.

  • No se pueden encontrar las bibliotecas de Python: compruebe que la ruta de acceso de Enlazador > General > Directorios de bibliotecas adicionales de las propiedades del proyecto apunta a la carpeta libs de instalación de Python.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. Consulte el paso 6 en Crear el proyecto de C++ principal.See step 6 under Create the core C++ project.

  • Errores del enlazador relacionados con la arquitectura de destino: cambie la arquitectura del proyecto del destino de C++ para que coincida con la de la instalación de Python.Linker errors related to target architecture: change the C++ target's project architecture to match that of your Python installation. Por ejemplo, si tiene como destino x64 con el proyecto de C++, pero la instalación de Python es x86, cambie el proyecto de C++ para que tenga como destino x86.For example, if you're targeting x64 with the C++ project but your Python installation is x86, change the C++ project to target x86.

Probar el código y comparar los resultadosTest the code and compare the results

Ahora que tiene los archivos DLL estructurados como extensiones de Python, puede hacerles referencia desde el proyecto de Python, importar los módulos y usar sus métodos.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.

Poner el archivo DLL a disposición de PythonMake the DLL available to Python

Hay dos maneras de hacer que el archivo DLL esté disponible para Python.There are two ways to make the DLL available to Python.

El primer método funciona si el proyecto de Python y el de C++ se encuentran en la misma solución.The first method works if the Python project and the C++ project are in the same solution. Vaya al Explorador de soluciones, haga clic con el botón derecho en el nodo Referencias del proyecto de Python y, después, seleccione Agregar referencia.Go to Solution Explorer, right-click the References node in your Python project, and then select Add Reference. En el cuadro de diálogo que aparecerá, seleccione la pestaña Proyectos, los proyectos superfastcode y superfastcode2 y Aceptar.In the dialog that appears, select the Projects tab, select both the superfastcode and superfastcode2 projects, and then select OK.

Agregar una referencia al proyecto superfastcode

El método alternativo, que se describe en los pasos siguientes, instala el módulo en el entorno global de Python, lo que hace que esté disponible también para otros proyectos de Python.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. Normalmente, realizar esta acción requiere actualizar la base de datos de finalización de IntelliSense para ese entorno en Visual Studio 2017, versión 15.5 y anteriores.(Doing so typically requires that you refresh the IntelliSense completion database for that environment in Visual Studio 2017 version 15.5 and earlier. También es necesario actualizar al quitar el módulo del entorno.Refreshing is also necessary when removing the module from the environment.)

  1. Si usa Visual Studio 2017, ejecute el instalador de Visual Studio, seleccione Modificar, seleccione Componentes individuales > Compiladores, herramientas de compilación y entornos de ejecución > Conjunto de herramientas de Visual C++ 2015.3 v140.If you're using Visual Studio 2017, run the Visual Studio installer, select Modify, select Individual Components > Compilers, build tools, and runtimes > Visual C++ 2015.3 v140 toolset. Este paso es necesario porque que Python (para Windows) se compila con Visual Studio 2015 (versión 14.0) y espera que esas herramientas estén disponibles al compilar una extensión mediante el método descrito aquí.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. (tenga en cuenta que puede que necesite instalar una versión de 32 bits de Python y establecer el DLL como Win32 y no como x64).(Note that you may need to install a 32-bit version of Python and target the DLL to Win32 and not x64.)

  2. Cree un archivo denominado setup.py en el proyecto de C++; para ello, haga clic con el botón derecho en el proyecto y seleccione Agregar > Nuevo elemento.Create a file named setup.py in the C++ project by right-clicking the project and selecting Add > New Item. Luego, seleccione Archivo C++ (.cpp), asigne el nombre setup.py al archivo y seleccione Aceptar (al nombrar el archivo con la extensión .py, Visual Studio lo reconoce como Python aunque use la plantilla de archivos de C++).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). Cuando aparezca el archivo en el editor, pegue el código siguiente en él tal y como sea adecuado para el método de extensión:When the file appears in the editor, paste the following code into it as appropriate to the extension method:

    Extensiones de CPython (proyecto superfastcode):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]
        )
    

    Vea Building C and C++ Extensions (Compilación de extensiones de C y C++) en python.org para obtener la documentación de este script.See Building C and C++ extensions (python.org) for documentation on this script.

    PyBind11 (proyecto superfastcode2):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. El código setup.py le indica a Python que debe compilar la extensión que usa el conjunto de herramientas de C++ de Visual Studio 2015 cuando se use desde la línea de comandos.The setup.py code instructs Python to build the extension using the Visual Studio 2015 C++ toolset when used from the command line. Abra un símbolo del sistema con privilegios elevados, vaya a la carpeta que contiene el proyecto de C++ (es decir, la carpeta que contenga setup.py) y escriba el comando siguiente: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 .
    

    O bienor:

    py -m pip install .
    

Llamar al archivo DLL desde PythonCall the DLL from Python

Una vez que el archivo DLL está disponible para Python como se ha descrito en la sección anterior, ahora puede llamar a las funciones superfastcode.fast_tanh y superfastcode2.fast_tanh2 del código de Python y comparar su rendimiento con la implementación de Python: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. Agregue las líneas siguientes en el archivo .py para llamar a los métodos exportados desde los archivos DLL y mostrar los resultados.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. Ejecute el programa de Python (Depurar > Iniciar sin depurar o Ctrl+F5) y observe que las rutinas de C++ se ejecutan aproximadamente de cinco a veinte veces más rápido que la implementación de Python.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. El resultado típico se muestra de la manera siguiente: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
    

    Si el comando Iniciar sin depurar está deshabilitado, haga clic con el botón derecho en el proyecto de Python en el Explorador de soluciones y seleccione Establecer como proyecto de inicio.If the Start Without Debugging command is disabled, right-click the Python project in Solution Explorer and select Set as Startup Project.

  3. Intente aumentar la variable COUNT para que las diferencias sean más pronunciadas.Try increasing the COUNT variable so that the differences are more pronounced. Una compilación de depuración del módulo de C++ también se ejecuta de forma más lenta que una compilación de lanzamiento, ya que la compilación de depuración está menos optimizada y contiene varias comprobaciones de errores.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. No dude en alternar entre dichas configuraciones para compararlas.Feel free to switch between those configurations for comparison.

Note

En la salida, puede ver que la extensión de PyBind11 no es tan rápida como la extensión de CPython, aunque sigue siendo mucho más rápida que la implementación directa de Python.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. La diferencia se debe a una pequeña cantidad de sobrecarga de cada llamada que presenta PyBind11 a fin de simplificar considerablemente su interfaz de C++.The difference is due to a small amount of per-call overhead that PyBind11 introduces in order to make its C++ interface dramatically simpler. Esta diferencia por llamada es realmente bastante insignificante: dado que el código de prueba llama a las funciones de extensión 500 000 veces, los resultados que ve aquí amplifican en gran medida esa sobrecarga.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! Por lo general, una función de C++ realiza mucho más trabajo que los métodos fast_tanh[2] triviales usados aquí, en cuyo caso no tiene importancia la sobrecarga.Typically, a C++ function does much more work than the trivial fast_tanh[2] methods used here, in which case the overhead is unimportant. En cambio, si va a implementar métodos que podrían llamarse miles de veces por segundo, mediante el enfoque de CPython puede conseguir un rendimiento mejor que PyBind11.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.

Depurar el código de C++Debug the C++ code

Visual Studio admite la depuración de código Python y C++ de forma conjunta.Visual Studio supports debugging Python and C++ code together. En esta sección, se le guía a través del proceso mediante el proyecto superfastcode; los pasos son los mismos para el proyecto superfastcode2.This section walks through the process using the superfastcode project; the steps are the same for the superfastcode2 project.

  1. Haga clic con el botón derecho en el proyecto de Python en el Explorador de soluciones, seleccione Propiedades, elija la pestaña Depurar y, después, seleccione la opción Depurar > Habilitar depuración de código nativo.Right-click the Python project in Solution Explorer, select Properties, select the Debug tab, and then select the Debug > Enable native code debugging option.

    Tip

    Cuando se habilita la depuración de código nativo, la ventana de resultados de Python podría desaparecer de inmediato al completarse el programa, sin ofrecer la pausa habitual Presione cualquier tecla para continuar.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. Para forzar una pausa, agregue la opción -i al campo Ejecutar > Argumentos del intérprete en la pestaña Depurar al habilitar la depuración de código nativo.To force a pause, add the -i option to the Run > Interpreter Arguments field on the Debug tab when you enable native code debugging. Este argumento coloca al intérprete de Python en modo interactivo cuando finaliza el código, momento en que espera a que presione Ctrl+Z > Entrar para salir.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. (Como alternativa, si no le importa modificar el código de Python, puede agregar las instrucciones import os y os.system("pause") al final del programa.(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. Este código duplica la petición de pausa original).This code duplicates the original pause prompt.)

  2. Seleccione Archivo > Guardar para guardar los cambios de propiedad.Select File > Save to save the property changes.

  3. Establezca la configuración de compilación como Depurar en la barra de herramientas de Visual Studio.Set the build configuration to Debug in the Visual Studio toolbar.

    Establecer la configuración de compilación como Depurar

  4. Puesto que el código normalmente tarda más tiempo en ejecutarse en el depurador, puede que le interese cambiar la variable COUNT del archivo .py a un valor que sea unas cinco veces más pequeño (por ejemplo, cambiarlo de 500000 a 100000).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. En el código de C++, establezca un punto de interrupción en la primera línea del método tanh_impl e inicie el depurador (F5 o Depurar > Iniciar depuración).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). El depurador se detendrá cuando se realice la llamada a ese código.The debugger stops when that code is called. Si no se alcanza el punto de interrupción, compruebe que la configuración esté establecida como Depurar y que haya guardado el proyecto (esto no se realiza automáticamente al iniciar al depurador).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).

    Detener en un punto de interrupción en el código de C++

  6. En este punto puede recorrer el código de C++, examinar las variables, etc.At this point you can step through the C++ code, examine variables, and so on. Estas características se detallan en Depurar Python y C++ de forma conjunta.These features are detailed in Debug C++ and Python together.

Enfoques alternativosAlternative approaches

Existen diversos métodos para crear extensiones de Python, como se describe en la tabla siguiente.There are a variety of means to create Python extensions as described in the following table. Las dos primeras entradas de CPython y PyBind11 son lo que ya se ha tratado en este artículo.The first two entries for CPython and PyBind11 are what has been discussed in this article already.

EnfoqueApproach AñoVintage Usuarios representativosRepresentative user(s) VentajasPro(s) InconvenientesCon(s)
Módulos de extensión de C/C++ para CPythonC/C++ extension modules for CPython 19911991 biblioteca estándarStandard Library Amplia documentación y tutoriales.Extensive documentation and tutorials. Control total.Total control. Compilación, portabilidad, administración de referencias.Compilation, portability, reference management. Extensos conocimientos de C.High C knowledge.
PyBind11 (recomendado para C++)PyBind11 (Recommended for C++) 20152015 Biblioteca ligera de solo encabezados para crear los enlaces de Python de código de C++ existente.Lightweight, header-only library for creating Python bindings of existing C++ code. Pocas dependencias.Few dependencies. Compatibilidad con PyPy.PyPy compatibility. Más reciente, menos maduro.Newer, less mature. Uso intensivo de características de C++11.Heavy use of C++11 features. Lista reducida de compiladores compatibles (incluye Visual Studio).Short list of supported compilers (Visual Studio is included).
Cython (recomendado para C)Cython (Recommnded for C) 20072007 gevent, kivygevent, kivy Semejante a Python.Python-like. Muy maduro.Highly mature. Alto rendimiento.High performance. Compilación, nueva sintaxis y nueva cadena de herramientas.Compilation, new syntax, new toolchain.
Boost.PythonBoost.Python 20022002 Funciona con casi todos los compiladores de C++.Works with just about every C++ compiler. Conjunto grande y complejo de bibliotecas; contiene muchas soluciones alternativas para los compiladores anteriores.Large and complex suite of libraries; contains many workarounds for old compilers.
ctypesctypes 20032003 oscryptooscrypto Sin compilación, amplia disponibilidad.No compilation, wide availability. El acceso y la mutación de estructuras de C son complicados y propensos a errores.Accessing and mutating C structures cumbersome and error prone.
SWIGSWIG 19961996 crfsuitecrfsuite Generar enlaces para muchos lenguajes a la vez.Generate bindings for many languages at once. Sobrecarga excesiva si Python es el único destino.Excessive overhead if Python is the only target.
cfficffi 20132013 cryptography, pypycryptography, pypy Facilidad de integración, compatibilidad con PyPy.Ease of integration, PyPy compatibility. Más reciente, menos maduro.Newer, less mature.
cppyycppyy 20172017 Similar a cffi con C++.Similar to cffi using C++. Más reciente, puede tener algunos problemas con Visual Studio 2017.Newer, may have some issues with VS 2017.

Vea tambiénSee also

El ejemplo completo de este tutorial se puede encontrar en python-samples-vs-cpp-extension (GitHub).The completed sample from this walkthrough can be found on python-samples-vs-cpp-extension (GitHub).