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

Este artículo, creará un módulo de extensión de C++ para CPython para calcular la tangente hiperbólica y realizar una llamada desde el código de Python. Primero, se implementa la rutina en Python para demostrar la mejora relativa del rendimiento de la implementación de la misma rutina en C++.

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

  • Módulos de acelerador: habilitan el rendimiento acelerado. Dado que Python es un lenguaje interpretado, puede escribir un módulo de acelerador en C++ para un mayor rendimiento.
  • 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.
  • Módulos de acceso al sistema de bajo nivel: cree módulos de acceso al sistema para acceder a características de bajo nivel del tiempo de ejecución de CPython, el sistema operativo o el hardware subyacente.

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

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

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

Requisitos previos

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

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

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

    Nota:

    Al instalar la carga de trabajo Aplicaciones de ciencia de datos y de análisis, se instalan de forma predeterminada Python y la opción Herramientas de desarrollo nativo de Python.

  • Si instala Python por separado, asegúrese de seleccionar Descargar símbolos de depuración en Opciones avanzadas en el programa de instalación 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 de Python.

  1. Cree un proyecto de Python en Visual Studio. Para ello, seleccione 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 Ubicación y seleccione Crear.

    Visual Studio crea el nuevo proyecto. El proyecto se abre en el Explorador de soluciones y el archivo del 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 matemática, y es lo que se acelerará luego con las extensiones nativas de Python.

    Sugerencia

    Escriba el código en Python puro antes de volver a escribirlo en C++. De este modo, puede comprobar más fácilmente que el código de Python nativo 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. Para ejecutar el programa, seleccione Depurar>Iniciar sin depuración 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 debería tardar aproximadamente 2 segundos.

  7. Según sea necesario, ajuste el valor de la variable COUNT 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 COUNT modificado genera la prueba comparativa en unos 2 segundos.

Sugerencia

Al ejecutar pruebas comparativas, use siempre la opción Depurar>Iniciar sin depuración. Este método ayuda a evitar la sobrecarga que se puede generar al ejecutar el código dentro del depurador de Visual Studio.

Crear los proyectos principales de C++

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

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

    Una solución de Visual Studio puede contener proyectos de Python y C++, una de las ventajas de utilizar 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 su nuevo proyecto, escriba el Nombre del proyecto:

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

Asegúrese de repetir estos pasos y cree 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 tutorial de este artículo, partiremos de un proyecto vacío para ayudar a mostrar cómo crear el módulo de extensión paso a paso. Después de comprender el proceso, puede usar la plantilla alternativa para ahorrar tiempo cuando escriba extensiones propias.

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 código fuente y seleccione Agregar>Nuevo elemento.

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

  3. Para el Nombre del archivo introduzca module.cpp y, a continuación, seleccione Agregar.

    Importante

    Asegúrese de que el nombre del archivo incluya 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:

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

    • Para un entorno de ejecución de Python de 64 bits, active la configuración de 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 las configuraciones de creación de depuración y versión de cada módulo.

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

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

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

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

    1. En Configuración, seleccione Depurar o Versión. (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 que hizo en el paso anterior.

      Nota:

      Al crear sus propios proyectos, querrá configurar las configuraciones de depuración y versión por separado, de acuerdo con los requisitos específicos del escenario. En este ejercicio, establecerá las configuraciones para usar una creación de versión de CPython. Esta configuración deshabilita algunas características de depuración del tiempo de ejecución de C++, incluidas las aserciones. Para usar archivos binarios de depuración de CPython (python_d.exe) se necesita otra configuración.

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

      Para cambiar un valor de propiedad, introduzca 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 Valor
      Propiedades de configuración>General Nombre de destino Especifique el nombre del módulo como quiera referirse a él desde Python en las instrucciones from...import, 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)>. Para python_d.exe, agregue _d al final del nombre.
      Tipo de configuración Biblioteca dinámica (.dll)
      Propiedades de configuración>Avanzado 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 sea necesario para la instalación; (por ejemplo, c:\Python36\include).
      C/C++>Preprocesador Definiciones de preprocesador Si está presente, cambie el valor _DEBUG por NDEBUG para que coincida con la versión que no es de depuración de CPython. Si usa python_d.exe, no cambie este valor.
      C/C++>Generación de código Biblioteca en tiempo de ejecución DLL multiproceso (/MD) para que coincida con la opción de versión (no de depuración) de CPython. Si usa python_d.exe, deje este valor como DLL de depuración multiproceso (/MDd).
      Comprobaciones en tiempo de ejecución básicas Predeterminado
      Vinculador>General Directorios de bibliotecas adicionales Agregue la carpeta libs de Python que contiene archivos .lib según sea necesario para la instalación (por ejemplo, c:\Python36\libs). Asegúrese de apuntar a la carpeta libs que contiene los archivos .lib, y no a la carpeta Lib que contiene los archivos .py.

      Importante

      Si la pestaña C/C++ no se muestra como opción para las propiedades del proyecto, significa que el proyecto no contiene ningún archivo de código que Visual Studio identifique como archivos de código fuente de C/C++. Esta condición se puede producir si crea un archivo de código fuente sin una extensión .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 de 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 creación.

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

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

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

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

      Los archivos .pyd están en la carpeta solución en Depuración y Versión, no en la carpeta del proyecto de C++.

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

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

  1. Para el proyecto de C++ 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. Cree 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 creación de versión.

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

Para convertir el archivo DLL de C++ en una extensión para Python, primero debe modificar los métodos exportados para que interactúen con tipos de Python. Después, debe agregar una función para exportar el módulo, junto con las 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 las extensiones CPython

Para obtener más información sobre el código que se presenta muestra en esta sección, consulte el Manual de referencia de la API de Python/C y, en concreto, 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++ 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. Sustituya el código del método tanh_impl para aceptar y devolver tipos de Python (es decir, un 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 tanh_impl de C++ 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 se debe hacer referencia al módulo en el código de Python, en concreto cuando use la instrucción from...import.

    El nombre que se importa en este código debe coincidir con el valor de las propiedades del proyecto en Propiedades de configuraciónGeneralNombre del destino>General>Nombre del destino.

    En el ejemplo siguiente, el nombre "superfastcode" significa que puede usar la instrucción from superfastcode import fast_tanh en Python, porque fast_tanh está definido en superfastcode_methods. Los nombres de archivo que son internos del proyecto de C++, como module.cpp, no son esenciales.

    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 llame cuando cargue el módulo. El nombre del método debe ser PyInit_<module-name>, donde <module-name> coincide exactamente con la propiedad Propiedades de configuración>General>Nombre de destino del proyecto de C++. Es decir, el nombre del método coincide con el nombre del archivo .pyd creado por el proyecto.

    PyMODINIT_FUNC PyInit_superfastcode() {
        return PyModule_Create(&superfastcode_module);
    }
    
  7. Cree el proyecto de C++ y verifique el código. Si se producen errores, consulte la sección Solución de problemas de creación.

Uso de PyBind11

Si completa los pasos de la sección anterior para el proyecto superfastcode, puede que observe que el ejercicio requiere código reutilizable para crear las estructuras de módulo para las extensiones de CPython de C++. 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, vea 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 ventanaHerramientas>Línea de comandos>PowerShell para desarrolladores.

  2. En la ventana PowerShell para desarrolladores, instale PyBind11 mediante el comando pip pip install pybind11 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 PowerShell para desarrolladores, ejecute el comando python -m pybind11 --includes o py -m pybind11 --includes.

    Esta acción imprime una lista de las rutas de acceso de PyBind11 que necesita agregar a las propiedades del 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.

    Screenshot that shows how to highlight and copy the list of paths from the Developer PowerShell window in 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 Versión. (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 PowerShell para desarrolladores:

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

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

      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 de 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 emergente para ir a la ubicación de la ruta de acceso.

      Importante

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

      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 que la macro PYBIND11_MODULE 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. Cree el proyecto de C++ y verifique el código. Si se producen errores, consulte la sección siguiente, Solución de problemas de creación.

Solución de problemas de creación

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

Error: no se puede encontrar el archivo de encabezado

Visual Studio devuelve un mensaje de error de tipo E1696: no se puede abrir el archivo de código fuente "Python.h" o C1083: no se puede abrir el archivo de inclusión: "Python.h": no existe el 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 proyecto C/C++>General>Directorios de inclusión adicionales 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. Revise los pasos de Adición de rutas de acceso de PyBind al proyecto.

Para obtener más información sobre cómo acceder a la información de configuración de la instalación de Python, consulte la Documentación de Python.

Error: No se pueden encontrar las 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 Enlazador>General>Directorios de bibliotecas adicionales contenga la ruta de acceso a la carpeta libs de la instalación de Python. Revise los pasos descritos en Configuración de las propiedades del proyecto.

Para obtener más información sobre cómo acceder a la información de configuración de la 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 hacerles referencia desde el proyecto de Python, importar los módulos y usar sus métodos.

Poner el archivo DLL a disposición de Python

Puede hacer que el archivo DLL esté disponible para Python de varias maneras. Estas son dos opciones que hay que 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 ficha Proyectos.

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

    Screenshot that shows how to add a reference to the super fast code project in 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. En el campo Nombre del archivo introduzca setup.py y, a continuación, seleccione Agregar.

    Asegúrese de escribir el nombre de archivo con la extensión (.py) de Python. 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 archivo nuevo 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 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 crear la extensión, haga clic con el botón derecho en el nombre del archivo pyproject.toml, en la pestaña de la ventana de código, y seleccione Copiar ruta de acceso completa.

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

    Eliminará el nombre pyproject.toml de la ruta de acceso antes de usarla.

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

  8. Haga clic con el botón derecho en el entorno de Python (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 situada junto al nombre del paquete para desinstalarlo.

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

  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.

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

  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 permisos, agregue el argumento --user al final del comando y vuelva a intentar la instalación.

Llamar al archivo DLL desde Python

Después de hacer que el archivo DLL esté disponible para Python, como se describe en la sección anterior, estará 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.

Siga estos pasos para llamar a la DLL del módulo de extensión desde Python:

  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 que se han exportado 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. Para ejecutar el programa de Python, seleccione Depurar>Iniciar sin depuración o utilice el método abreviado de teclado Ctrl+F5.

    Nota:

    Si el comando Iniciar sin depurar no está disponible, haga clic con el botón derecho en el proyecto de Python en el Explorador de soluciones 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 variable COUNT para que las diferencias de tiempo sean más pronunciadas.

    Una creación de depuración del módulo de C++ también se ejecuta de forma más lenta que una creación de versión, ya que la de depuración está menos optimizada y contiene varias comprobaciones de errores. Pruebe a cambiar entre las configuraciones de creación para compararlas, pero recuerde actualizar las propiedades que ha establecido antes 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 de PyBind11 no es tan rápida como la 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 a los autores de la llamada una interfaz más similar a Python. Dado que el código de prueba llama 500 000 veces a la función, es posible que los resultados aumenten considerablemente la sobrecarga.

Podría reducir todavía más la sobrecarga si mueve el bucle for al código nativo de Python. Este enfoque supone usar el protocolo de iterador (o el tipo py::iterable de PyBind11 para el parámetro de función) a fin de procesar cada elemento. Quitar las transiciones repetidas entre Python y C++ es una manera eficaz de reducir el tiempo que se necesita para procesar la secuencia.

Solución de problemas de importación

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

  • Al crear a través de una referencia de proyecto, asegúrese de que las propiedades del proyecto de C++ coincidan 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 tenga 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 comando pip en el entorno de Python activado para el proyecto de Python. Al expandir el entorno activo de Python para el proyecto en el Explorador de soluciones, debería ver una entrada para el proyecto de C++, como superfastcode.

Depuración de código de C++

Visual Studio admite la depuración de código Python y C++ de forma conjunta. Los pasos siguientes muestran el proceso de depuración para el proyecto de C++ 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 Depurar>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 de inmediato al completarse el programa, sin pausarse y mostrando el mensaje Presione cualquier tecla para continuar. Para forzar la pausa y el mensaje 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 pone al intérprete de Python en modo interactivo después de ejecutar 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 del programa de Python. Este código duplica la petición de pausa original.

  3. Seleccione Archivo>Guardar (o Ctrl+S) para guardar los cambios de propiedad.

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

  5. Como normalmente el código tarda más tiempo en ejecutarse en el depurador, puede que le interese cambiar la variable COUNT del archivo .py del proyecto de Python por un valor que sea unas cinco veces más pequeño que el predeterminado. Por ejemplo, puede cambiarlo de 500 000 a 100 000.

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

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

    El depurador se detendrá cuando se llame al punto de interrupción. Si no se alcanza el punto de interrupción, compruebe que la configuración esté establecida en Depuración y que ha guardado el proyecto, lo que no se realiza automáticamente al iniciar al depurador.

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

  8. En el punto de interrupción, puede ejecutar paso a paso el código de C++, examinar las variables, etc. Para obtener más información sobre estas características, vea Depuración conjunta de Python y C++.

Enfoques alternativos

Existen diversos métodos para crear extensiones de Python, como se describe en la tabla siguiente. En este artículo se describen las dos primeras filas, CPython y PyBind11.

Enfoque Vintage 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 cryptography, pypy
SWIG 1996 crfsuite
Boost.Python 2002
cppyy 2017