Создание расширения C++ для PythonCreate a C++ extension for Python

Модули, написанные на C++ (или C), обычно используются для расширения возможностей интерпретатора Python, а также для доступа к низкоуровневым возможностям операционной системы.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. Существует три основных типа модулей:There are three primary types of modules:

  • Модули акселератора: так как Python — это интерпретируемый язык, для повышения производительности некоторые фрагменты кода могут быть написаны на C++.Accelerator modules: because Python is an interpreted language, certain pieces of code can be written in C++ for higher performance.
  • Модули оболочки: предоставляют существующие интерфейсы C/C++ для кода Python или адаптированный API, который удобно использовать в Python.Wrapper modules: expose existing C/C++ interfaces to Python code or expose a more "Pythonic" API that's easy to use from Python.
  • Модули низкоуровневого системного доступа: созданы для доступа к низкоуровневым функциям среды выполнения CPython, операционной системы и базового оборудования.Low-level system access modules: created to access lower-level features of the CPython runtime, the operating system, or the underlying hardware.

В этой статье рассматривается создание модуля расширения C++ для CPython, вычисляющего гиперболический тангенс и вызывающего его из кода Python.This article walks through building a C++ extension module for CPython that computes a hyperbolic tangent and calls it from Python code. Подпрограмма реализована сначала на языке Python, чтобы продемонстрировать относительный прирост производительности по сравнению с реализацией той же подпрограммы на C++.The routine is implemented first in Python to demonstrate the relative performance gain of implementing the same routine in C++.

В этой статье также показаны два способа сделать C++ доступным для Python:This article also demonstrates two ways to make the C++ available to Python:

Сравнение этих и других средств описано в разделе Альтернативные подходы в конце этой статьи.A comparison between these and other means is found under alternative approaches at the end of this article.

Полный пример из этого руководства: python-samples-vs-cpp-extension (GitHub).The completed sample from this walkthrough can be found on python-samples-vs-cpp-extension (GitHub).

Предварительные требованияPrerequisites

  • Visual Studio 2017 или более поздней версии с установленными рабочими нагрузками Разработка классических приложений на C++ и Разработка на Python с параметрами по умолчанию.Visual Studio 2017 or later with both the Desktop Development with C++ and Python Development workloads installed with default options.

  • В рабочей нагрузке Разработка на Python также установите флажок справа для параметра Собственные средства разработки Python.In the Python Development workload, also select the box on the right for Python native development tools. Этот параметр позволяет настроить большую часть конфигурации, описанной в этой статье.This option sets up most of the configuration described in this article. (Этот параметр также автоматически включает рабочую нагрузку C++.)(This option also includes the C++ workload automatically.)

    Выбор параметра "Собственные средства разработки Python"

    Tip

    При установке рабочей нагрузки Приложения для обработки и анализа данных и аналитические приложения по умолчанию включаются среда Python и параметр Собственные средства разработки Python.Installing the Data science and analytical applications workload also includes Python and the Python native development tools option by default.

Дополнительные сведения, в том числе об использовании других версий Visual Studio, см. в руководстве по установке поддержки Python для Visual Studio.For more information, see Install Python support for Visual Studio, including using other versions of Visual Studio. Если вы устанавливаете Python отдельно, обязательно выберите параметры Download debugging symbols (Скачать отладочные символы) и Download debug binaries (Скачать двоичные файлы отладки) в разделе Дополнительные параметры установщика.If you install Python separately, be sure to select Download debugging symbols and Download debug binaries under Advanced Options in the installer. Это гарантирует наличие необходимых отладочных библиотек, если вы решите сделать отладочную сборку.This option ensures that you have the necessary debug libraries available if you choose to do a debug build.

Создание приложения PythonCreate the Python application

  1. Создайте проект Python в Visual Studio, выбрав Файл > Создать > Проект.Create a new Python project in Visual Studio by selecting File > New > Project. Выполните поиск слова "Python", выберите шаблон Приложение Python, назначьте ему подходящее имя и расположение, а затем нажмите кнопку ОК.Search for "Python", select the Python Application template, give it a suitable name and location, and select OK.

  2. Для работы с C++ необходимо использовать 32-разрядный интерпретатор Python (рекомендуемая версия — Python 3.6 или более поздняя версия).Working with C++ requires that you use a 32-bit Python interpreter (Python 3.6 or above recommended). В окне обозревателя решений в Visual Studio разверните узел проекта, а затем — узел окружения Python.In the Solution Explorer window of Visual Studio, expand the project node, then expand the Python Environments node. Если 32-разрядное окружение не указано как окружение по умолчанию (не выделено жирным шрифтом или меткой глобальное значение по умолчанию), следуйте инструкциям по выбору окружения Python для проекта.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. Если у вас не установлена 32-разрядная версия интерпретатора, см. инструкции по установке интерпретаторов Python.If you don't have a 32-bit interpreter installed, see Install Python interpreters.

  3. В файл .py проекта вставьте следующий код, отвечающий за тестирование производительности при вычислении гиперболического тангенса (для упрощения сравнения реализуется без использования математической библиотеки).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). Вы можете ввести код вручную, чтобы оценить некоторые функции редактирования 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. Запустите программу с помощью команды Отладка > Запуск без отладки (или нажмите клавиши CTRL+F5), чтобы просмотреть результаты.Run the program using Debug > Start without Debugging (Ctrl+F5) to see the results. Чтобы изменить длительность выполнения тестов производительности, можно настроить переменную COUNT.You can adjust the COUNT variable to change how long the benchmark takes to run. В целях этого руководства задайте значение так, чтобы тест производительности занимал приблизительно две секунды.For the purposes of this walkthrough, set the count so that the benchmark take around two seconds.

Tip

При выполнении тестов производительности всегда выбирайте Отладка > Запуск без отладки во избежание издержки при выполнении кода в отладчике Visual Studio.When running benchmarks, always use Debug > Start without Debugging to avoid the overhead incurred when running code within the Visual Studio debugger.

Создание основных проектов C++Create the core C++ projects

Следуйте инструкциям в этом разделе, чтобы создать два идентичных проекта C++ с именами superfastcode и superfastcode2.Follow the instructions in this section to create two identical C++ projects named "superfastcode" and "superfastcode2". Позже вы используете в каждом проекте разные способы предоставления кода C++ для Python.Later you'll use different means in each project to expose the C++ code to Python.

  1. Убедитесь, что в переменной среды PYTHONHOME указан путь к интерпретатору Python, который вы собираетесь использовать.Make sure the PYTHONHOME environment variable is set to the Python interpreter you want to use. Проекты C++ в Visual Studio используют эту переменную для поиска различных файлов, например python.h, используемых при создании модулей расширения для 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. Щелкните решение в обозревателе решений правой кнопкой мыши и выберите Добавить > Новый проект.Right-click the solution in Solution Explorer and select Add > New Project. Решение Visual Studio может одновременно содержать проекты Python и C++ (одно из преимуществ использования Visual Studio для 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. Выполните поиск "C++", выберите Пустой проект, укажите имя superfastcode (superfastcode2 для второго проекта) и выберите ОК.Search on "C++", select Empty project, specify the name "superfastcode" ("superfastcode2" for the second project), and select OK.

    Tip

    Если вы уже установили собственные средства разработки Python в Visual Studio, то можете начать с шаблона Модуль расширения Python, который уже содержит большую часть описываемых ниже функций.With the Python native development tools installed in Visual Studio, you can start with the Python Extension Module template instead, which has much of what's described below already in place. Но в этом пошаговом руководстве начало работы с пустого проекта позволяет шаг за шагом продемонстрировать создание модуля расширения.For this walkthrough, though, starting with an empty project demonstrates building the extension module step by step. Когда вы освоите процесс, шаблон позволит вам сэкономить время при написании собственных расширений.Once you understand the process, the template saves you time when writing your own extensions.

  4. Создайте файл C++ в новом проекте. Для этого щелкните правой кнопкой мыши узел Исходные файлы и выберите Добавить > Новый элемент, затем выберите Файл C++, присвойте ему имя module.cpp и нажмите кнопку ОК.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

    Файл с расширением .cpp нужен, чтобы активировать страницы свойств C++ в последующих шагах.A file with the .cpp extension is necessary to turn on the C++ property pages in the steps that follow.

  5. В обозревателе решений щелкните проект C++ правой кнопкой мыши и выберите пункт Свойства.Right-click the C++ project in Solution Explorer, select Properties.

  6. В верхней части появившегося диалогового окна Страницы свойств задайте для параметра Конфигурация значение Все конфигурации, а для параметра Платформа — Win32.At the top of the Property Pages dialog that appears, set Configuration to All Configurations and Platform to Win32.

  7. Задайте определенные свойства, как описано в таблице ниже, а затем нажмите кнопку ОК.Set the specific properties as described in the following table, then select OK.

    TabTab Свойство.Property ЗначениеValue
    Общие сведенияGeneral Общие > Целевое имяGeneral > Target Name Укажите имя модуля, которое будет использоваться из Python в инструкциях from...import.Specify the name of the module as you want to refer to it from Python in from...import statements. Это же имя используется в C++ при определении модуля для Python.You use this same name in the C++ when defining the module for Python. Если вы хотите применять имя проекта в качестве имени модуля, сохраните значение по умолчанию $(ProjectName).If you want to use the name of the project as the module name, leave the default value of $(ProjectName).
    Общие > Целевое расширениеGeneral > Target Extension .pyd.pyd
    Проект по умолчанию > Тип конфигурацииProject Defaults > Configuration Type Динамическая библиотека (.dll)Dynamic Library (.dll)
    C/C++ > ОбщиеC/C++ > General Дополнительные каталоги включаемых файловAdditional Include Directories Добавьте подходящую для вашей установки папку include Python, например c:\Python36\include.Add the Python include folder as appropriate for your installation, for example, c:\Python36\include.
    C/C++ > ПрепроцессорC/C++ > Preprocessor Определения препроцессораPreprocessor Definitions Только CPython. Добавьте Py_LIMITED_API; в начало строки (включая точку с запятой).CPython only: add Py_LIMITED_API; to the beginning of the string (including the semicolon). Это определение ограничивает некоторые функции, которые можно вызывать из Python, и расширяет возможности по переносу кода между разными версиями Python.This definition restricts some of the functions you can call from Python and makes the code more portable between different versions of Python. Если вы работаете с PyBind11, не добавляйте это определение. В противном случае вы увидите ошибки сборки.If you're working with PyBind11, don't add this definition, otherwise you'll see build errors.
    C/C++ > Создание кодаC/C++ > Code Generation Библиотека времени выполненияRuntime Library Многопоточная DLL (/MD) (см. предупреждение ниже)Multi-threaded DLL (/MD) (see Warning below)
    Компоновщик > ОбщиеLinker > General Дополнительные каталоги библиотекAdditional Library Directories Добавьте подходящую для вашей установки папку Python libs с файлами .lib, например c:\Python36\libs.Add the Python libs folder containing .lib files as appropriate for your installation, for example, c:\Python36\libs. (Обязательно укажите папку libs, содержащую файлы .lib, но не папку LIb, содержащую файлы .py.)(Be sure to point to the libs folder that contains .lib files, and not the Lib folder that contains .py files.)

    Tip

    Если вы не видите вкладку C/C++ в свойствах проекта, значит, в проекте нет файлов, определенных как исходные файлы 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. Такая ситуация может возникнуть, если вы создали исходный файл без расширения .c или .cpp.This condition can occur if you create a source file without a .c or .cpp extension. Например, если ранее вы случайно ввели module.coo вместо module.cpp в диалоговом окне создания элемента, Visual Studio создает файл, но не задает для него тип "Код C/C+", что требуется для активации вкладки свойств C/C++. Такая неправильная идентификация остается актуальной и в случае переименования файла с использованием .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. Чтобы правильно задать тип файла, в обозревателе решений щелкните файл правой кнопкой мыши, выберите Свойства, затем установите для параметра Тип файла значение Код 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

    Для параметра C/C++ > Создание кода > Библиотека времени выполнения обязательно задайте значение Многопоточная библиотека DLL (/MD) даже для конфигурации отладки, так как этот параметр используется при сборке неотладочных двоичных файлов 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. Если при использовании CPython вы задали параметр Многопоточная DLL с возможностью отладки (/MDd), при сборке конфигурации Отладка возникает ошибка C1189: Py_LIMITED_API несовместим с Py_DEBUG, Py_TRACE_REFS и Py_REF_DEBUG.With CPython, if you happen to set the Multi-threaded Debug DLL (/MDd) option, building a Debug configuration produces error C1189: Py_LIMITED_API is incompatible with Py_DEBUG, Py_TRACE_REFS, and Py_REF_DEBUG. Кроме того, если удалить Py_LIMITED_API (требуется при использовании CPython, но не PyBind11) во избежание этой ошибки сборки, Python аварийно завершает работу при попытке импортировать модуль.Furthermore, if you remove Py_LIMITED_API (which is required with CPython, but not PyBind11) to avoid the build error, Python crashes when attempting to import the module. (Этот сбой возникает в вызове библиотеки DLL PyModule_Create, как описано ниже, и сопровождается сообщением Неустранимая ошибка Python: PyThreadState_Get: нет текущего потока.)(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.)

    Параметр /MDd используется для сборки двоичных файлов отладки Python (например, python_d.exe), но его выбор для библиотеки DLL расширения по-прежнему вызывает ошибку сборки 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. Щелкните проект C++ правой кнопкой мыши и выберите Сборка, чтобы протестировать конфигурации (для отладки и выпуска).Right-click the C++ project and select Build to test your configurations (both Debug and Release). Файлы .pyd находятся в папке solution в каталогах Debug и Release, а не в самой папке проекта C++.The .pyd files are located in the solution folder under Debug and Release, not the C++ project folder itself.

  9. Добавьте следующий код в файл module.cpp проекта: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. Еще раз выполните сборку проекта C++, чтобы убедиться в правильности кода.Build the C++ project again to confirm that your code is correct.

  11. Если вы еще этого не сделали, повторите предыдущие действия, чтобы создать второй проект с именем superfastcode2 с идентичным содержимым.If you haven't already done so, repeat the steps above to create a second project named "superfastcode2" with identical contents.

Преобразование проекта C++ в расширения для PythonConvert the C++ projects to extensions for Python

Чтобы превратить библиотеку DLL на C++ в расширение для Python, сначала нужно изменить экспортированные методы для взаимодействия с типами Python.To make the C++ DLL into an extension for Python, you first modify the exported methods to interact with Python types. После этого нужно добавить функцию, экспортирующую модуль, а также определения методов модуля.You then add a function that exports the module, along with definitions of the module's methods.

В последующих разделах объясняется, как выполнять эти действия с помощью расширений CPython и PyBind11.The sections that follow explain how to perform these steps using both the CPython extensions and PyBind11.

Расширения CPythonCPython extensions

Пояснения к тому, что показано в этом разделе для Python 3.x, см. в справочном руководстве по API Python/C и, главным образом, в разделе Module Objects (Модульные объекты) на сайте python.org (не забудьте выбрать вашу версию Python в раскрывающемся списке в правом верхнем углу, чтобы увидеть подходящую документацию).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).

Если вы работаете с Python 2.7, см. статьи Extending Python 2.7 with C or C++ (Расширение Python 2.7 с помощью C и C++) и Porting Extension Modules to Python 3 (Перенос модулей расширения на 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. В верхнюю часть module.cpp включите Python.h:At the top of module.cpp, include Python.h:

    #include <Python.h>
    
  2. Измените метод tanh_impl, чтобы он принимал и возвращал типы 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. Добавьте структуру, определяющую способ представления функции tanh_impl C++ для 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. Добавьте структуру, которая определяет модуль так, как вы хотите ссылаться на него в своем коде Python, в частности, при использовании оператора 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. (Сделайте это значением в свойствах проекта в разделе Свойства конфигурации > Общие > Целевое имя.) В приведенном ниже примере имя модуля superfastcode означает, что from superfastcode import fast_tanh можно использовать в Python, так как функция fast_tanh определена в 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. (Внутренние для проекта C++ имена файлов, такие как module.cpp, являются несущественными.)(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. Добавьте метод, вызываемый Python при загрузке модуля. Он должен иметь имя PyInit_<module-name>, где <module_name> точно соответствует значению свойства Общие > Целевое имя проекта C++ (то есть соответствует имени файла .pyd, созданного проектом).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. Задайте целевую конфигурацию Выпуск и повторно создайте проект C++, чтобы проверить код.Set the target configuration to Release and build the C++ project again to verify your code. Если возникли ошибки, обратитесь к разделу Устранение проблем, расположенному ниже.If you encounter errors, see the Troubleshooting section below.

PyBind11PyBind11

Если вы выполнили действия, описанные в предыдущем разделе, вы наверняка заметили, что использовали много стандартного кода для создания необходимых структур модуля для кода 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 упрощает процесс с помощью макросов в файле заголовка C++, которые достигают того же результата с гораздо меньшим объемом кода.PyBind11 simplifies the process through macros in a C++ header file that accomplish the same result with much less code. Сведения о концепциях в этом разделе см. в разделе Основы PyBind11 (github.com).For background on what's shown in this section, see PyBind11 basics (github.com).

  1. Установите PyBind11 с помощью pip: pip install pybind11 или py -m pip install pybind11.Install PyBind11 using pip: pip install pybind11 or py -m pip install pybind11.

  2. В верхнюю часть module.cpp включите pybind11.h:At the top of module.cpp, include pybind11.h:

    #include <pybind11/pybind11.h>
    
  3. В нижней части module.cpp используйте макрос PYBIND11_MODULE, чтобы определить точку входа в функцию 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. Задайте целевую конфигурацию Выпуск и создайте проект C++, чтобы проверить код.Set the target configuration to Release and build the C++ project to verify your code. Если возникли ошибки, читайте следующий раздел об устранении неполадок.If you encounter errors, see the next section on troubleshooting.

Устранение неполадокTroubleshooting

Модуль C++ может не компилироваться по следующим причинам:The C++ module may fail to compile for the following reasons:

  • Не удалось найти Python.h (E1696: не удается открыть исходный файл Python.h и (или) C1083: не удается открыть включаемый файл: Python.h: отсутствует такой файл или каталог): убедитесь, что путь в разделе C/C++ > Общие > Дополнительные каталоги включаемых файлов в свойствах проекта указывает на папку include установки 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. См. шаг 6 в разделе Создание основного проекта C++.See step 6 under Create the core C++ project.

  • Не удается найти библиотеки Python. Убедитесь, что путь Компоновщик > Общие > Дополнительные каталоги библиотек в свойствах проекта указывает на папку libs установки 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. См. шаг 6 в разделе Создание основного проекта C++.See step 6 under Create the core C++ project.

  • Ошибки компоновщика, связанные с целевой архитектурой. Измените архитектуру целевого проекта C++ в соответствии с вашей установкой Python.Linker errors related to target architecture: change the C++ target's project architecture to match that of your Python installation. Например, если вы хотите использовать в проекте C++ версию Python x64, но у вас установлена версия x86, измените проект C++ для работы с версией х86.For example, if you're targeting x64 with the C++ project but your Python installation is x86, change the C++ project to target x86.

Тестирование кода и сравнение результатовTest the code and compare the results

Теперь, когда библиотека DLL структурирована как расширения Python, можно ссылаться на них из проекта Python, импортировать модули и использовать их методы.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.

Предоставление доступа к библиотеке DLL для PythonMake the DLL available to Python

Предоставить Python доступ к библиотеке DLL можно двумя способами.There are two ways to make the DLL available to Python.

Первый метод работает, если проект Python и проект C++ находятся в одном решении.The first method works if the Python project and the C++ project are in the same solution. В обозревателе решений щелкните правой кнопкой мыши узел Ссылки в проекте Python и затем выберите команду Добавить ссылку.Go to Solution Explorer, right-click the References node in your Python project, and then select Add Reference. В открывшемся диалоговом окне перейдите на вкладку Проекты, выберите проекты superfastcode и superfastcode2 и нажмите кнопку OK.In the dialog that appears, select the Projects tab, select both the superfastcode and superfastcode2 projects, and then select OK.

Добавление ссылки на проект superfastcode

Альтернативный метод, описанный далее, предполагает установку модуля в глобальной среде Python и предоставление к нему доступа в других проектах 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. (При этом обычно требуется обновить базу данных завершения IntelliSense для этой среды в Visual Studio 2017 версии 15.5 и более ранних версий.(Doing so typically requires that you refresh the IntelliSense completion database for that environment in Visual Studio 2017 version 15.5 and earlier. Обновление также требуется и при извлечении модуля из среды.)Refreshing is also necessary when removing the module from the environment.)

  1. Если вы используете Visual Studio 2017 или более поздней версии, запустите Visual Studio Installer, выберите Изменить, затем Отдельные компоненты > Компиляторы, средства сборки и среды выполнения > Набор инструментов Visual C++ 2015.3 версии 140.If you're using Visual Studio 2017 or later, run the Visual Studio installer, select Modify, select Individual Components > Compilers, build tools, and runtimes > Visual C++ 2015.3 v140 toolset. Этот шаг необходим, так как Python (для Windows) сам создан с помощью Visual Studio 2015 (версия 14.0) и поэтому ожидает наличия этих средств при создании расширения одним из описанных здесь способов.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. (Обратите внимание, что может потребоваться установить 32-разрядную версию Python и нацелить библиотеку DLL на Win32, а не на x64.)(Note that you may need to install a 32-bit version of Python and target the DLL to Win32 and not x64.)

  2. Создайте в проекте C++ файл с именем setup.py, щелкнув проект правой кнопкой мыши и выбрав пункт Добавить > Новый элемент.Create a file named setup.py in the C++ project by right-clicking the project and selecting Add > New Item. Выберите тип файла Файл C++ (.cpp), присвойте файлу имя setup.py и щелкните ОК. (Присвоение файлу расширения .py позволяет Visual Studio распознавать его как файл Python несмотря на использование шаблона файла 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). Когда файл откроется в редакторе, вставьте в него следующий код в зависимости от метода расширения:When the file appears in the editor, paste the following code into it as appropriate to the extension method:

    Расширения CPython (проект 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]
        )
    

    Документация к этому скрипту приведена в статье о создании расширений на C и C++ (python.org).See Building C and C++ extensions (python.org) for documentation on this script.

    PyBind11 (проект 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. При использовании в командной строке код setup.py предписывает Python создать расширение с помощью набора инструментов C++ Visual Studio 2015.The setup.py code instructs Python to build the extension using the Visual Studio 2015 C++ toolset when used from the command line. Откройте командную строку с повышенными привилегиями, перейдите в папку с проектом C++ (то есть в папку, содержащую setup.py), а затем введите следующую команду: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 .
    

    илиor:

    py -m pip install .
    

Вызов библиотеки DLL из PythonCall the DLL from Python

После того как вы предоставили библиотеки DLL для Python, как описано в предыдущем разделе, вы можете вызвать функции superfastcode.fast_tanh и superfastcode2.fast_tanh2 из кода Python и сравнить их эффективность с реализацией 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. Добавьте приведенные ниже строки в файл .py, чтобы вызвать методы, экспортированные из библиотек DLL, и отобразить их выходные данные: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. Запустите программу Python (Отладка > Запуск без отладки или CTRL+F5) и убедитесь, что подпрограмма C++ выполняется примерно в 5–20 раз быстрее, чем реализация 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. Обычно выводится следующий результат.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
    

    Если команда Запуск без отладки недоступна, в обозревателе решений щелкните правой кнопкой мыши проект Python и выберите команду Назначить запускаемым проектом.If the Start Without Debugging command is disabled, right-click the Python project in Solution Explorer and select Set as Startup Project.

  3. Попробуйте увеличить значение переменной COUNT, чтобы разница стала еще очевиднее.Try increasing the COUNT variable so that the differences are more pronounced. Кроме того, отладочная сборка модуля C++ выполняется медленнее, чем сборка выпуска, так как отладочная сборка менее оптимизирована и включает в себя разные проверки ошибок.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. Вы можете свободно переключаться между этими конфигурациями для их сравнения.Feel free to switch between those configurations for comparison.

Note

В выходных данных вы увидите, что расширение PyBind11 не такое быстрое, как расширение CPython, хотя все равно значительно быстрее, чем прямая реализация на языке 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. Разница связана с небольшим количеством издержек для отдельных вызовов, которые использует PyBind11, чтобы значительно упростить интерфейс 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. Это различие для одного вызова почти незаметно, но так как тестовый код вызывает функции расширения 500 000 раз, издержки накапливаются.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! Как правило, функция C++ выполняет гораздо больше работы, чем обычные методы fast_tanh[2], используемые здесь, в этом случае издержки не имеют значения.Typically, a C++ function does much more work than the trivial fast_tanh[2] methods used here, in which case the overhead is unimportant. Но если вы реализуете методы, которые могут вызываться тысячи раз в секунду, подход CPython будет иметь лучшую производительность по сравнению с 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.

Отладка кода C++Debug the C++ code

Visual Studio поддерживает совместную отладку кода на Python и C++.Visual Studio supports debugging Python and C++ code together. В этом разделе этот процесс рассматривается на примере проекта superfastcode. Для проекта superfastcode2 действия будут аналогичными.This section walks through the process using the superfastcode project; the steps are the same for the superfastcode2 project.

  1. Щелкните проект Python правой кнопкой мыши в обозревателе решений, выберите Свойства, откройте вкладку Отладка и выберите Отладка > Разрешить отладку машинного кода.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

    При включении отладки машинного кода окно вывода Python может исчезнуть сразу после завершения программы без обычной паузы с сообщением Для продолжения нажмите любую клавишу.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. Чтобы сделать паузу принудительно, добавьте параметр -i в поле Запуск > Аргументы интерпретатора на вкладке Отладка при включении отладки машинного кода.To force a pause, add the -i option to the Run > Interpreter Arguments field on the Debug tab when you enable native code debugging. Этот аргумент переводит интерпретатор Python в интерактивный режим по завершении кода, после чего он ожидает нажатия клавиш CTRL+Z > ВВОД для выхода.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. (Кроме того, если вы не против изменения кода Python, можно добавить в конец программы операторы import os и os.system("pause").(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. Этот код дублирует исходный запрос с паузой.)This code duplicates the original pause prompt.)

  2. Чтобы сохранить изменения свойств, выберите пункт меню Файл > Сохранить.Select File > Save to save the property changes.

  3. На панели инструментов Visual Studio измените конфигурацию сборки на режим Отладка.Set the build configuration to Debug in the Visual Studio toolbar.

    Изменение конфигурации сборки на режим "Отладка"

  4. Так как в отладчике код, как правило, выполняется дольше, может потребоваться уменьшить значение переменной COUNT в файле .py приблизительно в пять раз (например, вместо 500000 укажите 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. В коде C++ установите точку останова в первой строке метода tanh_impl, а затем запустите отладчик (F5 или Отладка > Начать отладку).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). При вызове этого кода отладчик останавливается.The debugger stops when that code is called. Если точка останова не сработает, проверьте, выбрана ли конфигурация отладки и сохранен ли проект (при запуске отладчика он не сохраняется автоматически).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).

    Остановка на точке останова в коде C++

  6. На этом этапе вы можете пошагово выполнять код на C++, проверять переменные и т. д.At this point you can step through the C++ code, examine variables, and so on. Эти возможности подробно описаны в руководстве по совместной отладке C++ и Python.These features are detailed in Debug C++ and Python together.

Альтернативные подходыAlternative approaches

Существуют различные средства для создания расширений Python, как описано в таблице ниже.There are a variety of means to create Python extensions as described in the following table. Первые две записи для CPython и PyBind11 уже обсуждались в этой статье.The first two entries for CPython and PyBind11 are what has been discussed in this article already.

ПодходApproach ПоявлениеVintage ПредставителиRepresentative user(s) ПреимуществаPro(s) НедостаткиCon(s)
Модули расширений C/C++ для CPythonC/C++ extension modules for CPython 19911991 Стандартная библиотекаStandard Library Подробная документация и учебники.Extensive documentation and tutorials. Полный контроль.Total control. Компиляция, переносимость, управление ссылками.Compilation, portability, reference management. Хорошее знание C.High C knowledge.
PyBind11 (рекомендуется для C++)PyBind11 (Recommended for C++) 20152015 Упрощенная библиотека заголовков для создания привязок Python к существующему коду C++.Lightweight, header-only library for creating Python bindings of existing C++ code. Малое число зависимостей.Few dependencies. Совместимость с PyPy.PyPy compatibility. Новый, менее проверенный подход.Newer, less mature. Частое использование функций C++11.Heavy use of C++11 features. Краткий список поддерживаемых компиляторов (включает Visual Studio).Short list of supported compilers (Visual Studio is included).
Cython (рекомендуется для C)Cython (Recommended for C) 20072007 gevent, kivygevent, kivy Аналогичен Python.Python-like. Высокая степень зрелости.Highly mature. Высокая производительность.High performance. Компиляция, новый синтаксис, новая цепочка инструментов.Compilation, new syntax, new toolchain.
Boost.PythonBoost.Python 20022002 Работает практически с любым компилятором C++.Works with just about every C++ compiler. Большой и сложный набор библиотек. Содержит много обходных путей для старых компиляторов.Large and complex suite of libraries; contains many workarounds for old compilers.
ctypesctypes 20032003 oscryptooscrypto Отсутствие компиляции, широкая доступность.No compilation, wide availability. Обращение к структурам C и их изменение затруднено и подвержено ошибкам.Accessing and mutating C structures cumbersome and error prone.
SWIGSWIG 19961996 crfsuitecrfsuite Создание привязок сразу для нескольких языков.Generate bindings for many languages at once. Чрезмерные затраты, когда единственной целью является Python.Excessive overhead if Python is the only target.
cfficffi 20132013 cryptography, pypycryptography, pypy Простота интеграции, совместимость с PyPy.Ease of integration, PyPy compatibility. Новый, менее проверенный подход.Newer, less mature.
cppyycppyy 20172017 Подобен cffi при использовании C++.Similar to cffi using C++. Новый, возможны проблемы с VS 2017.Newer, may have some issues with VS 2017.

См. такжеSee also

Полный пример из этого руководства: python-samples-vs-cpp-extension (GitHub).The completed sample from this walkthrough can be found on python-samples-vs-cpp-extension (GitHub).