Compartir a través de


Creación de una extensión de C++ para Python en Visual Studio

En este artículo, creas un módulo de extensión de C++ en CPython para calcular una tangente hiperbólica y llamarlo desde código Python. La rutina se implementa primero en Python para demostrar la ganancia de rendimiento relativa de la implementación de la misma rutina en C++.

Los módulos de código escritos en C++ (o C) se usan normalmente para ampliar las funcionalidades de un intérprete de Python. Hay tres tipos principales de módulos de extensión:

  • Módulos de acelerador: habilite el rendimiento acelerado. Dado que Python es un lenguaje interpretado, puede escribir un módulo de aceleración en C++ para obtener un mayor rendimiento.
  • Módulos contenedor: exponga las interfaces de C/C++ existentes al código de Python o exponga una API más similar a Python que sea fácil de usar desde Python.
  • Módulos de acceso de sistema de bajo nivel: cree módulos de acceso al sistema para alcanzar características de nivel inferior del CPython entorno de ejecución, el sistema operativo o el hardware subyacente.

En este artículo se muestran dos maneras de hacer que un módulo de extensión de C++ esté disponible para Python:

  • Use las extensiones estándar CPython , como se describe en la documentación de Python.
  • Use PyBind11, que se recomienda para C++11 debido a su simplicidad. Para garantizar la compatibilidad, asegúrese de que está trabajando con una de las versiones más recientes de Python.

El ejemplo completado de este tutorial está disponible en GitHub en python-samples-vs-cpp-extension.

Prerrequisitos

  • Visual Studio 2017 o posterior, con la carga de trabajo desarrollo de Python instalada. La carga de trabajo incluye las herramientas de desarrollo nativo de Python, que agregan la carga de trabajo de C++ y los conjuntos de herramientas necesarios para las extensiones nativas.

    Captura de pantalla de una lista de opciones de desarrollo de Python, en la que se resalta la opción Herramientas de desarrollo nativas de Python.

    Para obtener más información sobre las opciones de instalación, consulte Instalación de compatibilidad con Python para Visual Studio.

    Nota:

    Al instalar la carga de trabajo Ciencia de datos y aplicaciones analíticas , python y la opción herramientas de desarrollo nativas de Python se instalan de forma predeterminada.

  • Si instala Python por separado, asegúrese de seleccionar Descargar símbolos de depuración en Opciones avanzadas en el instalador de Python. Esta opción es necesaria para usar la depuración en modo mixto entre el código de Python y el código nativo.

Crear la aplicación de Python

Siga estos pasos para crear la aplicación python.

  1. Cree un nuevo proyecto de Python en Visual Studio seleccionando Archivo>Nuevo>Proyecto.

  2. En el cuadro de diálogo Crear un nuevo proyecto, busque python. Seleccione la plantilla Aplicación de Python y seleccione Siguiente.

  3. Escriba un nombre de proyecto y una ubicación y seleccione Crear.

    Visual Studio crea el nuevo proyecto. El proyecto se abre en el Explorador de soluciones y el archivo de proyecto (.py) se abre en el editor de código.

  4. En el archivo .py , pegue el código siguiente. Para experimentar algunas de las características de edición de Python, pruebe a escribir el código manualmente.

    Este código calcula una tangente hiperbólica sin usar la biblioteca de matemáticas, y es lo que se acelera más adelante con extensiones nativas de Python.

    Sugerencia

    Escriba el código en Python puro antes de volver a escribirlo en C++. De este modo, puede comprobar con más facilidad para asegurarse de que el código nativo de Python es correcto.

    from random import random
    from time import perf_counter
    
    # Change the value of COUNT according to the speed of your computer.
    # The value should enable the benchmark to complete in approximately 2 seconds.
    COUNT = 500000
    DATA = [(random() - 0.5) * 3 for _ in range(COUNT)]
    
    e = 2.7182818284590452353602874713527
    
    def sinh(x):
        return (1 - (e ** (-2 * x))) / (2 * (e ** -x))
    
    def cosh(x):
        return (1 + (e ** (-2 * x))) / (2 * (e ** -x))
    
    def tanh(x):
        tanh_x = sinh(x) / cosh(x)
        return tanh_x
    
    def test(fn, name):
        start = perf_counter()
        result = fn(DATA)
        duration = perf_counter() - start
        print('{} took {:.3f} seconds\n\n'.format(name, duration))
    
        for d in result:
            assert -1 <= d <= 1, " incorrect values"
    
    if __name__ == "__main__":
        print('Running benchmarks with COUNT = {}'.format(COUNT))
    
        test(lambda d: [tanh(x) for x in d], '[tanh(x) for x in d] (Python implementation)')
    
  5. Ejecute el programa seleccionando Depurar> o seleccione el método abreviado de teclado Ctrl+F5.

    Se abre una ventana de comandos para mostrar la salida del programa.

  6. En la salida, observe la cantidad de tiempo notificado para el proceso de prueba comparativa.

    Para este tutorial, el proceso de prueba comparativa debe tardar aproximadamente 2 segundos.

  7. Según sea necesario, ajuste el valor de la COUNT variable en el código para permitir que la prueba comparativa se complete en unos 2 segundos en el equipo.

  8. Vuelva a ejecutar el programa y confirme que el valor modificado COUNT genera el punto de referencia en unos 2 segundos.

Sugerencia

Al ejecutar pruebas comparativas, use siempre la opción Iniciar depuración>sin depurar . Este método ayuda a evitar la sobrecarga en las que puede incurrir al ejecutar el código en el depurador de Visual Studio.

Creación de los proyectos principales de C++

Siga estos pasos para crear dos proyectos idénticos de C++, superfastcode y superfastcode2. Más adelante, se usa un enfoque diferente en cada proyecto para exponer el código de C++ a Python.

  1. En el Explorador de soluciones, haga clic con el botón derecho en el nombre de la solución y seleccione Agregar>nuevo proyecto.

    Una solución de Visual Studio puede contener proyectos de Python y C++, que es una de las ventajas de usar Visual Studio para el desarrollo de Python.

  2. En el cuadro de diálogo Agregar un nuevo proyecto , establezca el filtro Lenguaje en C++y escriba vacío en el cuadro Buscar .

  3. En la lista de resultados de la plantilla de proyecto, seleccione Proyecto vacío y Siguiente.

  4. En el cuadro de diálogo Configurar el nuevo proyecto , escriba el nombre del proyecto:

    • En el primer proyecto, escriba el nombre superfastcode.
    • En el segundo proyecto, escriba el nombre superfastcode2.
  5. Selecciona Crear.

Asegúrese de repetir estos pasos y crear dos proyectos.

Sugerencia

Hay disponible un enfoque alternativo cuando tiene instaladas las herramientas de desarrollo nativo de Python en Visual Studio. Puede empezar con la plantilla Módulo de extensión de Python , que completa previamente muchos de los pasos descritos en este artículo.

Para el recorrido de este artículo, comenzar con un proyecto vacío ayuda a demostrar cómo compilar el módulo de extensión paso a paso. Después de comprender el proceso, puede usar la plantilla alternativa para ahorrar tiempo al escribir sus propias extensiones.

Adición de un archivo de C++ al proyecto

A continuación, agregue un archivo de C++ a cada proyecto.

  1. En el Explorador de soluciones, expanda el proyecto, haga clic con el botón derecho en el nodo Archivos de origen y seleccione Agregar>nuevo elemento.

  2. En la lista de plantillas de archivo, seleccione Archivo de C++ (.cpp).

  3. Escriba el nombre del archivo como module.cpp y seleccione Agregar.

    Importante

    Asegúrese de que el nombre de archivo incluye la extensión .cpp . Visual Studio busca un archivo con la extensión .cpp para habilitar la visualización de las páginas de propiedades del proyecto de C++.

  4. En la barra de herramientas, expanda el menú desplegable Configuración y seleccione el tipo de configuración de destino:

    Captura de pantalla que muestra cómo establecer el tipo de configuración de destino para el proyecto de C++ en Visual Studio.

    • Para un entorno de ejecución de Python de 64 bits, active la configuración x64 .
    • Para un entorno de ejecución de Python de 32 bits, active la configuración de Win32 .

Asegúrese de repetir estos pasos para ambos proyectos.

Configuración de las propiedades del proyecto

Antes de agregar código a los nuevos archivos de C++, configure las propiedades de cada proyecto de módulo de C++ y pruebe las configuraciones para asegurarse de que todo funciona.

Debe establecer las propiedades del proyecto para ambas configuraciones de compilación de depuración y compilación de lanzamiento de cada módulo.

  1. En el Explorador de soluciones, haga clic con el botón derecho en el proyecto del módulo de C++ (superfastcode o superfastcode2) y seleccione Propiedades.

  2. Configure las propiedades para la compilación de depuración del módulo y, a continuación, configure las mismas propiedades para la compilación de lanzamiento.

    En la parte superior del cuadro de diálogo Páginas de propiedades del proyecto, configure las siguientes opciones de configuración de archivo:

    Captura de pantalla que muestra cómo establecer las opciones de compilación y plataforma del proyecto para el archivo C++ en Visual Studio.

    1. En Configuración, seleccione Depurar o Liberar. (Es posible que vea estas opciones con el prefijo Activo ).

    2. En Plataforma, seleccione Activo (x64) o Activo (Win32), en función de la selección en el paso anterior.

      Nota:

      Al crear sus propios proyectos, querrá configurar las configuraciones de depuración y de publicación por separado, según sus necesidades específicas del escenario. En este ejercicio, establecerá la configuración para usar una compilación de lanzamiento de CPython. Esta configuración deshabilita algunas características de depuración del entorno de ejecución de C++, incluidas las aserciones. El uso de archivos binarios de depuración de CPython (python_d.exe) requiere una configuración diferente.

    3. Establezca otras propiedades del proyecto como se describe en la tabla siguiente.

      Para cambiar un valor de propiedad, escriba un valor en el campo de propiedad. Para algunos campos, puede seleccionar el valor actual para expandir un menú desplegable de opciones o abrir un cuadro de diálogo para ayudar a definir el valor.

      Después de actualizar los valores en una pestaña, seleccione Aplicar antes de cambiar a otra pestaña. Esta acción ayuda a garantizar que los cambios permanezcan.

      Pestaña y sección Propiedad Importancia
      Propiedades> de configuraciónGeneral Nombre de destino Especifique el nombre del módulo para hacer referencia a él desde Python en from...import instrucciones, como superfastcode. Use este mismo nombre en el código de C++ al definir el módulo para Python. Para usar el nombre del proyecto como nombre del módulo, deje el valor predeterminado de $<ProjectName>. En python_d.exe, agregue _d al final del nombre.
      Tipo de configuración Biblioteca dinámica (.dll)
      Propiedades> de configuraciónAvanzado Extensión de archivo de destino .pyd (módulo de extensión de Python)
      C/C++>General Directorios de inclusión adicionales Agregue la carpeta include de Python según corresponda para la instalación (por ejemplo, c:\Python36\include).
      C/C++>Preprocesador Definiciones del preprocesador Si está presente, cambie el valor _DEBUG a NDEBUG para que coincida con la versión no depurada de CPython. Cuando se usa python_d.exe, deje este valor sin cambios.
      C/C++>Generación de código Biblioteca en tiempo de ejecución DLL multiproceso (/MD) para que coincida con la versión de versión (no debug) de CPython. Cuando utilice python_d.exe, deje este valor como DLL de depuración multiproceso (/MDd).
      Comprobaciones básicas en tiempo de ejecución Predeterminado
      Enlazador>General Directorios de biblioteca adicionales Agregue la carpeta libs de Python que contiene archivos .lib , según corresponda para la instalación (por ejemplo, c:\Python36\libs). Asegúrese de apuntar a la carpeta libs que contiene archivos .lib y no a la carpeta Lib que contiene .py archivos.

      Importante

      Si la pestaña C/C++ no se muestra como opción para las propiedades del proyecto, el proyecto no contiene archivos de código que Visual Studio identifica como archivos de código fuente de C/C++. Esta condición puede producirse si crea un archivo de origen sin una extensión de archivo .c o .cpp .

      Si ha introducido accidentalmente module.coo en lugar de module.cpp al crear el archivo C++, Visual Studio crea el archivo, pero no establece el tipo de archivo en el compilador de C/C+. Este tipo de archivo es necesario para activar la presencia de la pestaña de propiedades de C/C++ en el cuadro de diálogo propiedades del proyecto. La identificación incorrecta permanece incluso si cambia el nombre del archivo de código con una extensión de archivo .cpp .

      Para establecer el tipo de archivo de código correctamente, en el Explorador de soluciones, haga clic con el botón derecho en el archivo de código y seleccione Propiedades. En Tipo de elemento, seleccione Compilador de C/C++.

    4. Después de actualizar todas las propiedades, seleccione Aceptar.

    Repita los pasos para la otra configuración de compilación.

  3. Pruebe la configuración actual. Repita los pasos siguientes para las compilaciones de depuración y de lanzamiento de ambos proyectos de C++.

    1. En la barra de herramientas de Visual Studio, establezca la configuración de compilación en Depurar o Liberar:

      Captura de pantalla que muestra cómo establecer la configuración de compilación para el proyecto de C++ en Visual Studio.

    2. En el Explorador de soluciones, haga clic con el botón derecho en el proyecto de C++ y seleccione Compilar.

      Los archivos .pyd se encuentran en la carpeta de la solución, bajo las carpetas Depuración y Publicación, y no en la propia carpeta del proyecto de C++.

Adición de código y configuración de prueba

Ahora estás listo para agregar código a tus archivos de C++ y probar la compilación de versión.

  1. Para el proyecto de C++ de superfastcode , abra el archivo module.cpp en el editor de código.

  2. En el archivo module.cpp , pegue el código siguiente:

    #include <Windows.h>
    #include <cmath>
    
    const double e = 2.7182818284590452353602874713527;
    
    double sinh_impl(double x) {
        return (1 - pow(e, (-2 * x))) / (2 * pow(e, -x));
    }
    
    double cosh_impl(double x) {
        return (1 + pow(e, (-2 * x))) / (2 * pow(e, -x));
    }
    
    double tanh_impl(double x) {
        return sinh_impl(x) / cosh_impl(x);
    }
    
  3. Guarde los cambios.

  4. Compile la configuración de versión del proyecto de C++ para confirmar que el código es correcto.

Repita los pasos para agregar código al archivo de C++ para el proyecto superfastcode2 y pruebe la compilación de versión .

Conversión de proyectos de C++ en extensiones de Python

Para que el archivo DLL de C++ sea una extensión para Python, primero modifique los métodos exportados para interactuar con los tipos de Python. A continuación, agregue una función para exportar el módulo, junto con definiciones para los métodos del módulo.

En las secciones siguientes se muestra cómo crear las extensiones mediante las extensiones de CPython y PyBind11. El proyecto superfasctcode usa las extensiones de CPython y el proyecto superfasctcode2 implementa PyBind11.

Uso de extensiones de CPython

Para obtener más información sobre el código presentado en esta sección, consulte El manual de referencia de la API de Python/C, especialmente la página Objetos de módulo . Al revisar el contenido de referencia, asegúrese de seleccionar la versión de Python en la lista desplegable de la parte superior derecha.

  1. Para el proyecto de C++ de superfastcode , abra el archivo module.cpp en el editor de código.

  2. Agregue una instrucción en la parte superior del archivo module.cpp para incluir el archivo de encabezado Python.h :

    #include <Python.h>
    
  3. Reemplace el código del método tanh_impl para aceptar y devolver tipos de Python (es decir, un objeto PyObject*).

    PyObject* tanh_impl(PyObject* /* unused module reference */, PyObject* o) {
        double x = PyFloat_AsDouble(o);
        double tanh_x = sinh_impl(x) / cosh_impl(x);
        return PyFloat_FromDouble(tanh_x);
    }
    
  4. Al final del archivo, agregue una estructura para definir cómo presentar la función de C++ tanh_impl a Python:

    static PyMethodDef superfastcode_methods[] = {
        // The first property is the name exposed to Python, fast_tanh
        // The second is the C++ function with the implementation
        // METH_O means it takes a single PyObject argument
        { "fast_tanh", (PyCFunction)tanh_impl, METH_O, nullptr },
    
        // Terminate the array with an object containing nulls
        { nullptr, nullptr, 0, nullptr }
    };
    
  5. Agregue otra estructura para definir cómo hacer referencia al módulo en el código de Python, específicamente cuando se usa la from...import instrucción .

    El nombre importado en este código debe coincidir con el valor de las propiedades del proyecto en Propiedades> de configuraciónNombre de destino>.

    En el ejemplo siguiente, el "superfastcode" nombre significa que puede usar la from superfastcode import fast_tanh instrucción en Python porque fast_tanh se define en superfastcode_methods. Los nombres de archivo que son internos para el proyecto de C++, como module.cpp, son inconsecuentes.

    static PyModuleDef superfastcode_module = {
        PyModuleDef_HEAD_INIT,
        "superfastcode",                        // Module name to use with Python import statements
        "Provides some functions, but faster",  // Module description
        0,
        superfastcode_methods                   // Structure that defines the methods of the module
    };
    
  6. Agregue un método al que Python llama cuando carga el módulo. El nombre del método debe ser PyInit_<module-name>, donde <module-name> coincide exactamente con la propiedad Configuración de propiedades>General>Nombre de destino del proyecto de C++. Es decir, el nombre del método coincide con el nombre de archivo del archivo .pyd compilado por el proyecto.

    PyMODINIT_FUNC PyInit_superfastcode() {
        return PyModule_Create(&superfastcode_module);
    }
    
  7. Compile el proyecto de C++ y compruebe el código. Si se producen errores, consulte la sección "Solucionar errores de compilación".

Uso de PyBind11

Si completas los pasos descritos en la sección anterior para el proyecto superfastcode, es posible que observes que el ejercicio requiere plantilla de código para crear las estructuras de módulo para las extensiones de C++ CPython. En este ejercicio, descubrirá que PyBind11 simplifica el proceso de codificación. Las macros se usan en un archivo de encabezado de C++ para lograr el mismo resultado, pero con mucho menos código. Sin embargo, se requieren pasos adicionales para asegurarse de que Visual Studio puede localizar las bibliotecas de PyBind11 e incluir archivos. Para obtener más información sobre el código de esta sección, consulte Conceptos básicos de PyBind11.

Instalación de PyBind11

El primer paso es instalar PyBind11 en la configuración del proyecto. En este ejercicio, usará la ventana de PowerShell para desarrolladores .

  1. Abra la ventana Herramientas>línea de comandos>PowerShell para desarrolladores.

  2. En la ventana de PowerShell para desarrolladores , instale PyBind11 mediante el comando pip install pybind11 pip o py -m pip install pybind11.

    Visual Studio instala PyBind11 y sus paquetes dependientes.

Adición de rutas de acceso de PyBind11 al proyecto

Después de instalar PyBind11, debe agregar las rutas de acceso de PyBind11 a la propiedad Directorios de inclusión adicionales para el proyecto.

  1. En Developer PowerShell , ejecute el comando python -m pybind11 --includes o py -m pybind11 --includes.

    Esta acción imprime una lista de rutas de PyBind11 que usted necesita agregar a las propiedades de su proyecto.

  2. Resalte la lista de rutas de acceso en la ventana y seleccione Copiar (página doble) en la barra de herramientas de la ventana.

    Captura de pantalla que muestra cómo resaltar y copiar la lista de rutas de acceso desde la ventana de PowerShell para desarrolladores de Visual Studio.

    La lista de rutas de acceso concatenadas se agrega al portapapeles.

  3. En el Explorador de soluciones, haga clic con el botón derecho en el proyecto superfastcode2 y seleccione Propiedades.

  4. En la parte superior del cuadro de diálogo Páginas de propiedades , en el campo Configuración , seleccione Liberar. (Es posible que vea esta opción con el prefijo Activo ).

  5. En el cuadro de diálogo, en la pestaña C/C++>General , expanda el menú desplegable de la propiedad Directorios de inclusión adicionales y seleccione Editar.

  6. En el cuadro de diálogo emergente, agregue la lista de rutas de acceso copiadas:

    Repita estos pasos para cada ruta de acceso de la lista concatenada copiada de la ventana de PowerShell para desarrolladores :

    1. Seleccione Nueva línea (carpeta con símbolo más) en la barra de herramientas del cuadro de diálogo emergente.

      Captura de pantalla que muestra cómo agregar una ruta de acceso de PyBind11 a la propiedad Directorios de inclusión adicionales.

      Visual Studio agrega una línea vacía en la parte superior de la lista de rutas de acceso y coloca el cursor de inserción al principio.

    2. Pegue la ruta de acceso PyBind11 en la línea vacía.

      También puede seleccionar Más opciones (...) y usar un cuadro de diálogo del explorador de archivos emergentes para ir a la ubicación de la ruta de acceso.

      Importante

      • Si la ruta contiene el prefijo -I, quite el prefijo de la ruta.
      • Para que Visual Studio reconozca una ruta, esta debe estar en una línea separada.

      Después de agregar una nueva ruta de acceso, Visual Studio muestra la ruta de acceso confirmada en el campo Valor evaluado .

  7. Seleccione Aceptar para salir del cuadro de diálogo emergente.

  8. En la parte superior del cuadro de diálogo Páginas de propiedades , mantenga el puntero sobre el valor de la propiedad Directorios de inclusión adicionales y confirme que las rutas de acceso de PyBind11 están presentes.

  9. Seleccione Aceptar para aplicar los cambios en las propiedades.

Actualización del archivo module.cpp

El último paso es agregar el archivo de encabezado PyBind11 y el código de macro al archivo C++ del proyecto.

  1. Para el proyecto de C++ superfastcode2 , abra el archivo module.cpp en el editor de código.

  2. Agregue una instrucción en la parte superior del archivo module.cpp para incluir el archivo de encabezado pybind11.h :

    #include <pybind11/pybind11.h>
    
  3. Al final del archivo module.cpp , agregue código para la PYBIND11_MODULE macro para definir el punto de entrada a la función de C++:

    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. Compile el proyecto de C++ y compruebe el código. Si se producen errores, consulte la sección siguiente, Solución de problemas de errores de compilación.

Solución de errores de compilación

Revise las secciones siguientes para ver los posibles problemas que pueden provocar un error en la compilación del módulo de C++.

Error: No se puede encontrar el archivo de encabezado

Visual Studio devuelve un mensaje de error como E1696: No se puede abrir el archivo de código abierto "Python.h" o C1083: No se puede abrir el archivo de inclusión: "Python.h": No se puede abrir este archivo o directorio.

Este error indica que el compilador no puede encontrar un archivo de encabezado (.h) necesario para el proyecto.

  • Para el proyecto superfastcode, compruebe que la propiedad del proyectoDirectorios de inclusión adicionales de >> contiene la ruta de acceso a la carpeta include de la instalación de Python. Revise los pasos descritos en Configuración de las propiedades del proyecto.

  • Para el proyecto superfastcode2 , compruebe que la misma propiedad del proyecto contiene la ruta de acceso a la carpeta include de la instalación de PyBind11. Revisa los pasos para Añadir rutas de PyBind al proyecto.

Para más información sobre el acceso a la información de configuración de instalación de Python, consulte la documentación de Python.

Error: No se pueden encontrar bibliotecas de Python

Visual Studio devuelve un error que indica que el compilador no puede encontrar los archivos de biblioteca (DLL) necesarios para el proyecto.

  • Para el proyecto de C++ (superfastcode o superfastcode2), compruebe que la propiedad Directorios de bibliotecas adicionales del >> contiene la ruta de acceso a la carpeta libs para la instalación de Python. Revise los pasos descritos en Configuración de las propiedades del proyecto.

Para más información sobre el acceso a la información de configuración de instalación de Python, consulte la documentación de Python.

Visual Studio notifica errores del enlazador relacionados con la configuración de la arquitectura de destino del proyecto, como x64 o Win32.

  • Para el proyecto de C++ (superfastcode o superfastcode2), cambie la configuración de destino para que coincida con la instalación de Python. Por ejemplo, si la configuración de destino del proyecto de C++ es Win32, pero la instalación de Python es de 64 bits, cambie la configuración de destino del proyecto de C++ a x64.

Probar el código y comparar los resultados

Ahora que tiene los archivos DLL estructurados como extensiones de Python, puede hacer referencia a ellos desde el proyecto de Python, importar los módulos y usar sus métodos.

Hacer que el archivo DLL esté disponible para Python

Puede hacer que el archivo DLL esté disponible para Python de varias maneras. Estas son dos opciones que se deben tener en cuenta:

Si el proyecto de Python y el proyecto de C++ están en la misma solución, puede usar el siguiente enfoque:

  1. En el Explorador de soluciones, haga clic con el botón derecho en el nodo Referencias del proyecto de Python y seleccione Agregar referencia.

    Asegúrese de realizar esta acción para el proyecto de Python y no para el proyecto de C++.

  2. En el cuadro de diálogo Agregar referencia , expanda la pestaña Proyectos .

  3. Active las casillas de los proyectos superfastcode y superfastcode2 y seleccione Aceptar.

    Captura de pantalla que muestra cómo agregar una referencia al proyecto de código super rápido en Visual Studio.

Un enfoque alternativo consiste en instalar el módulo de extensión de C++ en el entorno de Python. Este método hace que el módulo esté disponible para otros proyectos de Python. Para obtener más información, consulte la documentación del proyecto setuptools.

Complete los pasos siguientes para instalar el módulo de extensión de C++ en el entorno de Python:

  1. En el Explorador de soluciones, haga clic con el botón derecho en el proyecto de C++ y seleccione Agregar>nuevo elemento.

  2. En la lista de plantillas de archivo, seleccione Archivo de C++ (.cpp).

  3. Escriba el nombre del archivo como setup.py y, a continuación, seleccione Agregar.

    Asegúrese de escribir el nombre de archivo con la extensión python (.py). Visual Studio reconoce el archivo como código de Python a pesar del uso de la plantilla de archivo de C++.

    Visual Studio abre el nuevo archivo en el editor de código.

  4. Pegue el código siguiente en el nuevo archivo. Elija la versión de código correspondiente al método de extensión:

    • Extensiones de CPython (proyecto de superfastcode ):

      from setuptools import setup, Extension
      
      sfc_module = Extension('superfastcode', sources = ['module.cpp'])
      
      setup(
          name='superfastcode',
          version='1.0',
          description='Python Package with superfastcode C++ extension',
          ext_modules=[sfc_module]
      )
      
    • PyBind11 (proyecto superfastcode2 ):

      from setuptools import setup, Extension
      import pybind11
      
      cpp_args = ['-std=c++11', '-stdlib=libc++', '-mmacosx-version-min=10.7']
      
      sfc_module = Extension(
          'superfastcode2',
          sources=['module.cpp'],
          include_dirs=[pybind11.get_include()],
          language='c++',
          extra_compile_args=cpp_args,
      )
      
      setup(
          name='superfastcode2',
          version='1.0',
          description='Python package with superfastcode2 C++ extension (PyBind11)',
          ext_modules=[sfc_module],
      )
      
  5. En el proyecto de C++, cree un segundo archivo denominado pyproject.toml y pegue el código siguiente:

    [build-system]
    requires = ["setuptools", "wheel", "pybind11"]
    build-backend = "setuptools.build_meta"
    

    El archivo TOML (.toml) usa el formato de lenguaje obvio y mínimo de Tom para los archivos de configuración.

  6. Para compilar la extensión, haga clic con el botón derecho en el nombre de archivo pyproject.toml en la pestaña ventana de código y seleccione Copiar ruta de acceso completa.

    Captura de pantalla que muestra cómo copiar la ruta de acceso completa al archivo toml del proyecto py en Visual Studio.

    Elimine el nombre pyproject.toml de la ruta de acceso antes de usarlo.

  7. En el Explorador de soluciones, expanda el nodo Entornos de Python para la solución.

  8. Haga clic con el botón derecho en el entorno de Python activo (que se muestra en negrita) y seleccione Administrar paquetes de Python.

    Se abre el panel Entornos de Python .

    Si el paquete necesario ya está instalado, verá que aparece en este panel.

    • Antes de continuar, seleccione la X junto al nombre del paquete para desinstalarlo.

    Captura de pantalla que muestra cómo desinstalar un paquete en el panel Entornos de Python.

  9. En el cuadro de búsqueda del panel Entornos de Python , pegue la ruta de acceso copiada y elimine el nombre de archivo pyproject.toml del final de la ruta de acceso.

    Captura de pantalla que muestra cómo escribir la ruta de acceso en el panel Entornos de Python para instalar el módulo de extensión.

  10. Seleccione Entrar para instalar el módulo desde la ubicación de la ruta de acceso copiada.

    Sugerencia

    Si se produce un error en la instalación debido a un error de permiso, agregue el --user argumento al final del comando e inténtelo de nuevo.

Llamada al archivo DLL desde Python

Después de que el archivo DLL esté disponible para Python, como se describe en la sección anterior, ya está listo para llamar a las funciones superfastcode.fast_tanh y superfastcode2.fast_tanh2 desde Python. A continuación, puede comparar el rendimiento de la función con la implementación de Python.

En Python, siga estos pasos para llamar a la DLL del módulo de extensión:

  1. Abra el archivo .py del proyecto de Python en el editor de código.

  2. Al final del archivo, agregue el código siguiente para llamar a los métodos exportados desde los archivos DLL y mostrar su salida:

    from superfastcode import fast_tanh
    test(lambda d: [fast_tanh(x) for x in d], '[fast_tanh(x) for x in d] (CPython C++ extension)')
    
    from superfastcode2 import fast_tanh2
    test(lambda d: [fast_tanh2(x) for x in d], '[fast_tanh2(x) for x in d] (PyBind11 C++ extension)')
    
  3. Ejecute el programa de Python seleccionando Depurar> o use el método abreviado de teclado Ctrl+F5.

    Nota:

    Si el comando Iniciar sin depuración no está disponible, en el Explorador de soluciones, haga clic con el botón derecho en el proyecto de Python y seleccione Establecer como proyecto de inicio.

    Cuando se ejecute el programa, observe que las rutinas de C++ se ejecutan aproximadamente de 5 a 20 veces más rápido que la implementación de Python.

    Este es un ejemplo de salida típica del programa:

    Running benchmarks with COUNT = 500000
    [tanh(x) for x in d] (Python implementation) took 0.758 seconds
    
    [fast_tanh(x) for x in d] (CPython C++ extension) took 0.076 seconds
    
    [fast_tanh2(x) for x in d] (PyBind11 C++ extension) took 0.204 seconds
    
  4. Intente aumentar la COUNT variable para que las diferencias de tiempo sean más pronunciadas.

    Una compilación de depuración del módulo de C++ también se ejecuta más lentamente que una compilación de versión porque la compilación de depuración está menos optimizada y contiene varias comprobaciones de errores. Intente cambiar entre las configuraciones de compilación para la comparación, pero recuerde actualizar las propiedades que estableció anteriormente para la configuración de versión.

Abordar la velocidad y la sobrecarga del proceso

En la salida, es posible que observe que la extensión PyBind11 no es tan rápida como la extensión de CPython, aunque debería ser más rápida que la implementación pura de Python. La razón principal de la diferencia se debe al uso de la marca METH_O. Esta marca no admite varios parámetros, nombres de parámetros ni argumentos de palabras clave. PyBind11 genera código ligeramente más complejo para proporcionar una interfaz más similar a Python a los autores de llamadas. Dado que el código de prueba llama a la función 500 000 veces, los resultados pueden amplificar considerablemente la sobrecarga.

Puede reducir aún más la sobrecarga moviendo el for bucle al código nativo de Python. Este enfoque implica el uso del protocolo iterador (o el tipo PyBind11 py::iterable para el parámetro de función) para procesar cada elemento. Quitar las transiciones repetidas entre Python y C++ es una manera eficaz de reducir el tiempo necesario para procesar la secuencia.

Solución de errores de importación

Si recibe un ImportError mensaje al intentar importar el módulo, puede resolverlo de una de las maneras siguientes:

  • Al compilar a través de una referencia de proyecto, asegúrese de que las propiedades del proyecto de C++ coinciden con el entorno de Python activado para el proyecto de Python. Confirme que las mismas ubicaciones de carpeta están en uso para los archivos Include (.h) y Library (DLL).

  • Asegúrese de que el archivo de salida tiene el nombre correcto, como superfastcode.pyd. Un nombre o extensión incorrecto impide la importación del archivo necesario.

  • Si instala el módulo mediante el archivo setup.py , asegúrese de ejecutar el pip comando en el entorno de Python activado para el proyecto de Python. Al expandir el entorno de Python activo para el proyecto en el Explorador de soluciones, debería ver una entrada para el proyecto de C++, como superfastcode.

Depurar código de C++

Visual Studio admite la depuración de código de Python y C++ simultáneamente. Los pasos siguientes muestran el proceso de depuración para el proyecto de C++ de superfastcode , pero el proceso es el mismo para el proyecto superfastcode2 .

  1. En el Explorador de soluciones, haga clic con el botón derecho en el proyecto de Python y seleccione Propiedades.

  2. En el panel Propiedades , seleccione la pestaña Depurar y, a continuación, seleccione la opción Habilitar>depuración de código nativo .

    Sugerencia

    Al habilitar la depuración de código nativo, la ventana de salida de Python podría cerrarse inmediatamente después de que el programa finalice sin pausar ni mostrar la solicitud de Presionar cualquier tecla para continuar. Para forzar la pausa y el aviso después de habilitar la depuración de código nativo, agregue el argumento -i al campo Ejecutar>Argumentos del intérprete en la pestaña Depurar. Este argumento coloca el intérprete de Python en modo interactivo después de que se ejecute el código. El programa espera a que seleccione Ctrl+Z+Entrar para cerrar la ventana. Un enfoque alternativo consiste en agregar las instrucciones import os y os.system("pause") al final de su programa de Python. Este código duplica la solicitud de pausa original.

  3. Seleccione Guardar archivo>(oCtrl+S) para guardar los cambios de propiedad.

  4. En la barra de herramientas de Visual Studio, establezca la configuración de compilación en Depurar.

  5. Dado que el código suele tardar más tiempo en ejecutarse en el depurador, es posible que desee cambiar la COUNT variable del proyecto de Python .py archivo a un valor que sea aproximadamente cinco veces menor que el valor predeterminado. Por ejemplo, cámbielo de 500000 a100000.

  6. En el código de C++, establezca un punto de interrupción en la primera línea del tanh_impl método.

  7. Inicie el depurador seleccionando Depurar>Iniciar depuración o use el método abreviado de teclado F5.

    El depurador se detiene cuando se llama al código de punto de interrupción. Si no se alcanza el punto de interrupción, compruebe que la configuración está establecida en Depurar y que guardó el proyecto, que no se produce automáticamente al iniciar el depurador.

    Captura de pantalla del código de C++ que contiene un punto de interrupción en Visual Studio.

  8. En el punto de interrupción, puede recorrer el código de C++, examinar variables, etc. Para obtener más información sobre estas características, consulte Depurar Python y C++ juntos.

Enfoques alternativos

Puede crear extensiones de Python de varias maneras, como se describe en la tabla siguiente. Las dos primeras filas, CPython y PyBind11, se describen en este artículo.

Enfoque Antiguo Usuarios representativos
Módulos de extensión de C/C++ para CPython 1991 biblioteca estándar
PyBind11 (recomendado para C++) 2015
Cython (recomendado para C) 2007 gevent, kivy
HPy 2019
mypyc 2017
ctypes 2003 oscrypto
cffi 2013 criptografía, pypy
TRAGO 1996 crfsuite
Boost.Python 2002
cppyy 2017