Criar uma extensão C++ para o Python no Visual Studio

Neste artigo, veja como criar um módulo de extensão C++ para CPython que calcula uma tangente hiperbólica e faz uma chamada a ela do código Python. A rotina é implementada primeiro em Python para demonstrar o ganho de desempenho relativo da implementação da mesma rotina em C++.

Os módulos escritos em C++ (ou C) são normalmente usados para estender os recursos de um interpretador do Python. Há três tipos principais de módulos de extensão:

  • Módulos de acelerador: permitem um desempenho acelerado. Como Python é uma linguagem interpretada, você pode escrever módulos de acelerador no C++ para obter um desempenho mais alto.
  • Módulos de wrapper: expõem interfaces C/C++ existentes para o código Python ou expõem uma API mais "Pythonic" fácil de usar do Python.
  • Módulos de acesso de baixo nível do sistema: criam módulos de acesso ao sistema para acessar os recursos de baixo nível do runtime CPython, do sistema operacional ou do hardware subjacente.

Este artigo também demonstra duas maneiras de disponibilizar um módulo de extensão C++ para Python:

  • Utilize as extensões CPython padrão, conforme descrito na Documentção Python.
  • Utilize PyBind11, recomendamos para C++ 11 devido à sua simplicidade. Para garantir a compatibilidade, confirme que você esteja trabalhando com uma das versões mais recentes do Python.

O exemplo completo deste passo a passo está no GitHub em python-samples-vs-cpp-extension.

Pré-requisitos

  • O Visual Studio 2017 ou posterior com a carga de trabalho de desenvolvimento Python instalada. A carga de trabalho inclui as ferramentas de desenvolvimento nativo do Python, que adicionam a carga de trabalho do C++ e os conjuntos de ferramentas necessários para extensões nativas.

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

    Para obter mais informações sobre as opções de instalação, consulte Instalar o suporte do Python para Visual Studio.

    Observação

    Quando você instala a da carga de trabalho Aplicativos de ciência de dados e análise, o Python e a opção Ferramentas nativas de desenvolvimento em Python são instaladas por padrão.

  • Se você instalar o Python separadamente, lembre-se de selecionar Baixar símbolos de depuração em Opções Avançadas no instalador Python. Essa opção é necessária para que você use a depuração de modo misto entre o código Python e o código nativo.

Criar o aplicativo do Python

Siga estes passos para criar o aplicativo Python.

  1. Crie um novo projeto Python no Visual Studio escolhendo Arquivo>Novo>Projeto.

  2. Na caixa de diálogo Criar um novo projeto, pesquise por python. Selecione o modelo Aplicativo do Python e Avançar.

  3. sira um Nome do Projeto e Localização, e selecione Criar.

    O Visual Studio cria o projeto. O projeto é aberto no Gerenciador de Soluções e o arquivo de projeto (.py) é aberto no editor de códigos.

  4. o arquivo .py, cole o código a seguir. Para experimentar alguns recursos de edição do Python, tente inserir o código manualmente.

    Esse código calcula uma tangente hiperbólica sem usar a biblioteca de matemática e é isso que você vai acelerar com extensões nativas Python.

    Dica

    Escreva seu código em Python puro antes de reescrevê-lo em C++. Dessa forma, você pode verificar com mais facilidade se o código nativo Python está correto.

    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. Execute o programa selecionando Depurar>Iniciar sem Depuração ou selecionando Ctrl+F5.

    Uma janela de comando é aberta e mostra a saída do programa.

  6. Na saída, veja a quantidade de tempo relatada para o processo de benchmark.

    Para este passo a passo, o processo de benchmark deve levar cerca de 2 segundos.

  7. Conforme necessário, ajuste o valor da variável COUNT no código para permitir que o benchmark seja concluído em cerca de 2 segundos no computador.

  8. Execute o programa novamente e veja se o valor modificado COUNT produz o benchmark em cerca de 2 segundos.

Dica

Ao executar parâmetros de comparação, sempre use a opção Depuração>Iniciar sem Depuração. Este método ajuda a evitar a sobrecarga que você incorre ao executar o código no depurador do Visual Studio.

Criar os projetos principais de C++

Siga estes passos para criar dois projetos C++ idênticos, superfastcode e superfastcode2. Posteriormente, você usará uma abordagem diferente em cada projeto para expor o código C++ para Python.

  1. No Gerenciador de Soluções, clique com o botão direito do mouse no nó da solução e selecione Adicionar>Novo projeto.

    Uma solução do Visual Studio pode conter os projetos Python e C++, que é uma das vantagens de usar o Visual Studio para desenvolvimento Python.

  2. Na caixa de diálogo Adicionar um novo projeto, defina o filtro Idioma como C++, e insira vazio na caixa Pesquisar.

  3. a lista de resultados do modelo de projeto, selecione Projeto vazio, e selecione Avançar.

  4. Na caixa de diálogo Configurar seu novo projeto, insira o Nome do projeto:

    • Para o primeiro projeto, insira o nome superfastcode.
    • Para o segundo projeto, insira o nome superfastcode2.
  5. Selecione Criar.

Lembre-se de repetir esses passos e criar dois projetos.

Dica

Uma abordagem alternativa fica disponível quando você tem as ferramentas de desenvolvimento nativo Python instaladas no Visual Studio. Você pode começar com o modelo Módulo de extensão do Python, que pré-conclui vários passos descritos neste artigo.

Para o passo a passo deste artigo, começar com um projeto vazio ajuda a demonstrar como construir o módulo de extensão passo a passo. Depois de entender o processo, você pode usar o modelo alternativo para poupar tempo ao escrever suas próprias extensões.

Adicione o o arquivo C++ ao projeto

Depois, adicione um arquivo C++ a cada projeto.

  1. Em Gerenciador de Soluções, expanda o projeto, clique com o botão direito do mouse no nó Arquivos de origem e selecione Adicionar>Novo item.

  2. Na lista de modelos de arquivo, selecione o Arquivo C++ (.cpp).

  3. Insira o Nome para o arquivo como module.cpp, e selecione Adicionar.

    Importante

    Certifique-se de que o nome do arquivo inclua a extensão .cpp. O Visual Studio busca um arquivo com a extensão .cpp para habilitar a exibição das páginas de propriedades do projeto C++.

  4. Na barra de ferramentas, expanda o menu suspenso Configuração e selecione o tipo de configuração de destino:

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

    • Para um runtime do Python de 64 bits, ative a configuração x64.
    • Para um runtime do Python de 32 bits, ative a configuração do Win32.

Lembre-se de repetir esses passos para os dois projetos.

Configurar as propriedades do projeto

Antes de adicionar código aos novos arquivos C++, configure as propriedades de cada projeto de módulo C++ e teste as configurações para garantir que tudo funcione bem.

Você precisa definir as propriedades do projeto para as configurações de compilação de depuração e versão de cada módulo.

  1. No Gerenciador de Soluções, clique com o botão direito do mouse no projeto do módulo (superfastcode ou superfastcode2) e selecione Propriedades.

  2. Configure as propriedades para a compilação de depuração do módulo e, depois, configure as mesmas propriedades para a compilação de versão:

    Na parte superior da caixa de diálogo Páginas de propriedades do projeto, configure as seguintes opções de configuração de arquivo:

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

    1. Para a Configuração, selecione Depurar ou Versão. (Você poderá ver essas opções com o prefixo Ativo.)

    2. Para a Plataforma, selecione Ativo (x64) ou Ativo (Win32), dependendo da seleção na etapa anterior.

      Observação

      Ao criar seus próprios projetos, é recomendável definir as configurações de depuração e versão separadamente, de acordo com seus requisitos de cenário específicos. Neste exercício, você define as configurações para usar uma compilação da versão do CPython. Essa configuração desabilita alguns recursos de depuração do runtime do C++, incluindo asserções. O uso de binários de depuração do CPython (python_d.exe) requer configurações diferentes.

    3. Defina outras propriedades do projeto conforme descrito na tabela a seguir.

      Para mudar o valor de uma propriedade, insira um valor no campo de propriedade. Para alguns campos, você pode selecionar o valor atual para expandir o menu suspenso de opções ou abrir a caixa de diálogo para ajudar a definir o valor.

      Depois de atualizar os valores em uma guia, selecione Aplicar antes de alternar para outra guia. Essa ação ajuda a garantir que suas alterações permaneçam.

      Guia e seção Propriedade Valor
      Propriedades de Configuração>Geral Nome de destino Especifique o nome do módulo ao qual você deseja se referir do Python nas instruções from...import, como superfastcode. Você usa esse mesmo nome no código C++ ao definir o módulo para Python. Para usar o nome do projeto como o nome do módulo, deixe o valor padrão de $<ProjectName>. Para python_d.exe, adicione _d ao final do nome.
      Tipo de Configuração Biblioteca Dinâmica (.dll)
      Propriedades de Configuração>Avançada Extensão do arquivo de destino .pyd (Módulo de Extensão do Python)
      C/C++>Geral Diretórios de Inclusão Adicionais Adicione a pasta include do Python conforme apropriado para sua instalação (por exemplo, c:\Python36\include).
      C/C++>Pré-processador Definições do Pré-processador Se estiver presente, mude o valor _DEBUG para NDEBUG para corresponder à versão de não depuração do CPython. Quando você usar python_d.exe, deixe esse valor inalterado.
      C/C++>Geração de Código Biblioteca em Runtime DLL de vários threads (/MD) para corresponder à versão (não depuração) do CPython. Quando você usar python_d.exe, deixe esse valor como DLL de depuração multi-threaded (/MDd).
      Verificações básicas de runtime Padrão
      Vinculador>Geral Diretórios de Biblioteca Adicionais Adicione a pasta libs do Python que contém arquivos .lib conforme apropriado para sua instalação (por exemplo, c:\Python36\libs). Lembre-se de apontar para a pasta libs que contém arquivos .lib e não para a pasta Lib que contém arquivos .py.

      Importante

      Se a guia C/C++ não for exibida como opção para as propriedades do projeto, o projeto não conterá arquivo de código que o Visual Studio identifique como arquivos de origem C/C++. Essa condição poderá ocorrer se você criar um arquivo de origem sem uma extensão de arquivo .c ou .cpp.

      Se você inseriu acidentalmente module.coo em vez de module.cpp quando você criou o arquivo C++, o Visual Studio cria o arquivo, mas não define o tipo para Compilador C/C+. Esse tipo de arquivo é necessário para ativar a presença da guia Propriedades C/C++ na caixa de diálogo das propriedades do projeto. A identificação incorreta permanece mesmo que você renomeie o arquivo de código com uma extensão de arquivo .cpp.

      Para definir o tipo de arquivo de código corretamente, em Gerenciador de Soluções, clique com o botão direito do mouse no arquivo de código e selecione Propriedades. Para o Tipo de item, selecione Compilador C/C++.

    4. Depois de atualizar todas as propriedades, selecione OK.

    Repita estes passos para a outra configuração de build.

  3. Testar a configuração atual. Repita as etapas a seguir para as compilações depuração e versão dos dois projetos C++.

    1. Na barra de ferramentas do Visual Studio, defina a configuração de Compilação como Depurar ou Versão:

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

    2. No Gerenciador de Soluções, clique com o botão direito do mouse no projeto C++ e selecione Compilar.

      Os arquivos .pyd estão localizados na pasta solução em Depurar e Versão, e não na própria pasta do projeto do C++.

Adicionar código e testar configuração

Agora você está pronto para adicionar código aos seus arquivos C++ e testar a compilação da versão.

  1. Para o projeto C++ superfastcode, abra o arquivo module.cpp no editor de código.

  2. No arquivo module.cpp, cole estes código:

    #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. Salve suas alterações.

  4. Crie a configuração de versão para o projeto C++ para confirmar que o seu código esteja correto.

Repita as etapas para adicionar código ao arquivo C++ para o projeto superfastcode2 e teste a compilação versão.

Converter os projetos de C++ em uma extensão de Python

Para transformar a DLL C++ em uma extensão para Python, modifique primeiro os métodos exportados para interagir com os tipos Python. Depois, adicione uma função para exportar o módulo, juntamente com as definições dos métodos do módulo.

As seções a seguir mostram como criar as extensões usando as extensões CPython e PyBind11. O projeto superfasctcode usa as extensões CPython e o projeto superfasctcode2 implementa PyBind11.

Usar extensões CPython

Para mais informações sobre o código apresentado nesta seção, consulte o Manual de Referência da API do Python/C e, principalmente, a página Objetos de Módulo. Ao ler o conteúdo de referência, lembre-se de selecionar sua versão do Python na lista suspensa no canto superior direito.

  1. Para o projeto C++ superfastcode, abra o arquivo module.cpp no editor de código.

  2. Adicione uma instrução na parte superior do arquivo module.cpp para incluir o arquivo de cabeçalho Python.h:

    #include <Python.h>
    
  3. Substitua código do método tanh_impl para aceitar e retornar tipos Python (ou seja, um 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. Ao final do arquivo, adicione uma estrutura para definir como apresentar a função C++ tanh_impl ao 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. Adicione outra estrutura para definir como se referir ao módulo no seu código Python, especificamente quando você usa a instrução from...import.

    O nome que está sendo importado nesse código deve corresponder ao valor nas propriedades do projeto em Propriedades de Configuração>Geral>Nome de Destino.

    No exemplo a seguir, o nome "superfastcode" significa que você pode usar a declaração from superfastcode import fast_tanh no Python, pois fast_tanh é definido dentro de superfastcode_methods. Os nomes de arquivo internos do projeto C++, como module.cpp, não são importantes.

    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. Adicione um método que o Python chama ao carregar o módulo. O nome do método deve ser PyInit_<module-name>, em que <module-name> corresponde exatamente à propriedade Propriedades de Configuração>Geral>Nome de destino do projeto C++. Ou seja, o nome do método corresponde ao nome do arquivo .pyd criado pelo projeto.

    PyMODINIT_FUNC PyInit_superfastcode() {
        return PyModule_Create(&superfastcode_module);
    }
    
  7. Compile o projeto C++ e verifique seu código. Se encontrar erros, confira a seção "Solucionar erros de compilação".

Usar PyBind11

Se concluir as etapas na seção anterior para o projeto superfastcode, você poderá notar que o exercício requer o código clichê para criar as estruturas de módulo para extensões CPython C++. Neste exercício, você descobrirá que o PyBind11 simplifica o processo de codificação. Você usa macros em um arquivo de cabeçalho C++ para chegar ao mesmo resultado, mas com muito menos código. Porém, é preciso executar outros passos para garantir que o Visual Studio possa localizar as bibliotecas PyBind11 e incluir arquivos. Para obter mais informações sobre o código nesta seção, consulte Noções básicas do PyBind11.

Instalar o PyBind11

O primeiro passo é instalar o PyBind11 na configuração do seu projeto. Neste exercício, você usa a janela PowerShell do Desenvolvedor.

  1. Abra a janela Ferramentas>Linha de comando>PowerShell do desenvolvedor.

  2. Na janela PowerShell do desenvolvedor, instale PyBind11 usando o comando pip pip install pybind11 ou py -m pip install pybind11.

    O Visual Studio instala PyBind11 e seus pacotes dependentes.

Adicionar os caminhos PyBind11 ao projeto

Após a instalação do PyBind11, você precisa adicionar os caminhos à propriedade Diretórios de inclusão adicionais para o projeto.

  1. Na janela PowerShell do desenvolvedor, execute o comando python -m pybind11 --includes ou py -m pybind11 --includes.

    Essa ação imprime uma lista de caminhos PyBind11 que você deve adicionar às propriedades do projeto.

  2. Destaque a lista de caminhos na janela e selecione Copiar (página dupla) na barra de ferramentas da janela.

    Screenshot that shows how to highlight and copy the list of paths from the Developer PowerShell window in Visual Studio.

    A lista de caminhos concatenados é adicionada a sua área de transferência.

  3. No Gerenciador de Soluções, clique com o botão direito do mouse no projeto superfastcode2 e selecione Propriedades.

  4. Na parte superior da caixa de diálogo Páginas de propriedade, para o campo Configuração, selecione Versão. (Você poderá ver essa opção com o prefixo Ativo.)

  5. Na caixa de diálogo, na guia C/C++>Geral,expanda o menu suspenso para a propriedade Diretórios de inclusão adicionais e selecione Editar.

  6. Na caixa de diálogo de pop-up, adicione a lista de caminhos copiados:

    Repita estes passos para cada caminho na lista concatenada copiada da janela PowerShell do desenvolvedor:

    1. Selecione Nova Linha (pasta com símbolo de mais) na barra de ferramentas da caixa de diálogo pop-up.

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

      O Visual Studio adiciona uma linha vazia na parte superior da lista de caminhos e posiciona o cursor no início.

    2. Na linha vazia, cole o caminho PyBind11.

      Você também pode selecionar Mais opções (...) e usar uma caixa de diálogo pop-up do explorador de arquivos para navegar ao local do caminho.

      Importante

      • Se o caminho contiver o prefixo -I, remova o prefixo do caminho.
      • Para o Visual Studio reconhecer um caminho, o caminho precisa estar em uma linha separada.

      Depois de adicionar um novo caminho, o Visual Studio mostra o caminho confirmado no campo Valor avaliado.

  7. Selecione OK para sair da caixa de diálogo pop-up.

  8. Na parte superior da caixa de diálogo Páginas de propriedade, passe o mouse sobre o valor para a propriedade Diretórios de inclusão adicionais e confirme se os caminhos PyBind11 estão presentes.

  9. Selecione OK para aplicar as alterações de propriedade.

Atualizar o arquivo module.cpp

O último passo é adicionar o arquivo de cabeçalho PyBind11 e o código de macro ao arquivo C++ do projeto.

  1. Para o projeto C++ superfastcode2, abra o arquivo module.cpp no editor de código.

  2. Adicione uma instrução na parte superior do arquivo module.cpp para incluir o arquivo de cabeçalho pybind11.h:

    #include <pybind11/pybind11.h>
    
  3. No final do arquivo module.cpp, adicione o código para a macro PYBIND11_MODULE para definir o ponto de entrada para a função 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 o projeto C++ e verifique seu código. Se encontrar erros, confira a próxima seção Solucionar erros de compilação.

Solucionar erros de compilação

Analise as seções a seguir para ver os possíveis problemas que podem causar falha na compilação do módulo C++.

Erro: Não é possível localizar o arquivo do cabeçalho

O Visual Studio retorna uma mensagem de erro como E1696: Não é possível abrir o arquivo fonte "Python.h" ou C1083: Não é possível abrir o arquivo de inclusão: "Python.h": Nenhum arquivo ou diretório.

Esse erro indica que o compilador não consegue localizar um arquivo de cabeçalho (.h) necessário para seu projeto.

  • Para o projeto superfastcode, verifique se a propriedade de projeto C/C++>Geral>Diretórios de inclusão adicionais contém o caminho para a pasta incluir para a instalação do Python. Revise as etapas em Configurar propriedades do projeto.

  • Para o projeto superfastcode2, verifique se a mesma propriedade do projeto contém o caminho para a pasta incluir para a instalação do PyBind11. Revise as etapas Adicionar os caminhos PyBind11 ao projeto.

a obter mais informações sobre como acessar as informações de configuração de instalação do Python, consulte a Documentação do Python.

Erro: não é possível localizar bibliotecas do Python

O Visual Studio retorna um erro indicando que o compilador não pode localizar os arquivos de biblioteca (DLL) necessários para seu projeto.

  • Para o projeto C++ (superfastcode ou superfastcode2), verifique se a propriedade Vinculador>Geral>Diretórios de biblioteca adicionais contém o caminho para a pasta libs para sua instalação do Python. Revise as etapas em Configurar propriedades do projeto.

a obter mais informações sobre como acessar as informações de configuração de instalação do Python, consulte a Documentação do Python.

O Visual Studio relata erros do vinculador relacionados à configuração da arquitetura de destino para seu projeto, como x64 ou Win32.

  • Para o projeto C++ (superfastcode ou superfastcode2), altere a configuração de destino para corresponder à sua instalação do Python. Por exemplo, se a configuração de destino do projeto C++ for Win32, mas a instalação do Python for de 64 bits, mude a configuração de destino do projeto C++ para x64.

Testar o código e comparar os resultados

Agora que você tem as DLLs estruturadas como extensões de Python, é possível consultá-las por meio do projeto do Python, importar o módulo e usar seus métodos.

Disponibilize a DLL para o Python

Você pode disponibilizar a DLL para Python de diversas maneiras. Considere estas duas opções:

Se o projeto Python e o projeto C++ estiverem na mesma solução, você poderá usar esta abordagem:

  1. No Gerenciador de Soluções, clique com o botão direito do mouse no nó Referências em seu projeto do Python e selecione Adicionar referência.

    Lembre-se de executar essa ação para seu projeto Python e não para seu projeto C++.

  2. Na caixa de diálogo Adicionar referência, expanda a guia Projetos.

  3. Marque as caixas de seleção para os projetos superfastcode e superfastcode2 e selecione OK.

    Screenshot that shows how to add a reference to the super fast code project in Visual Studio.

Uma abordagem alternativa é instalar o módulo de extensão C++ no seu ambiente Python. Esse método disponibiliza o módulo para outros projetos Python. Para obter mais informações, confira a documentação do projeto setuptools.

Execute estes passos para instalar o módulo de extensão C++ em seu ambiente Python:

  1. No Gerenciador de Soluções, clique com o botão direito do mouse no nó do projeto e selecione Adicionar>Novo Item.

  2. Na lista de modelos de arquivo, selecione o Arquivo C++ (.cpp).

  3. Insira o Nome para o arquivo como setup.py, e selecione Adicionar.

    Lembre-se de inserir o nome do arquivo com a extensão Python (.py). O Visual Studio reconhece o arquivo como código Python, apesar do uso do modelo de arquivo C++.

    O Visual Studio abre o novo arquivo no editor de código.

  4. Cole o código seguinte no novo arquivo. Escolha a versão do código que corresponda ao seu método de extensão:

    • Extensões de CPython (projeto 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 (projeto 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. No projeto C++, crie um segundo arquivo chamado pyproject.toml e cole o seguinte código:

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

    O arquivo TOML (.toml) usa o formato Tom's Obvious, Minimal Language para arquivos de configuração.

  6. Para compilar a extensão, clique com o botão direito do mouse no nome de arquivo pyproject.toml na guia da janela do código e selecione Copiar caminho completo.

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

    Você exclui o nome pyproject.toml do caminho antes de usá-lo.

  7. No Gerenciador de soluções, expanda o nó Ambientes Python da solução.

  8. Clique com o botão direito no ambiente Python ativo (mostrado em negrito) e selecione Gerenciar Pacotes Python.

    O painel Ambientes Python é aberto.

    Se o pacote necessário já estiver instalado, você o verá neste painel.

    • Antes de continuar, selecione o X do lado do nome do pacote para desinstalá-lo.

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

  9. Na caixa de pesquisa do painel Ambientes Python, cole o caminho copiado e exclua o nome de arquivo pyproject.toml do final do caminho.

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

  10. Selecione Enter para instalar o módulo do local do caminho copiado.

    Dica

    Se a instalação falhar devido a um erro de permissão, adicione o argumento --user ao final do comando e tente a instalação novamente.

Chamar a DLL no Python

Depois de disponibilizar a DLL para o Python, conforme descrito na seção anterior, você estará pronto para chamar as funções superfastcode.fast_tanh e superfastcode2.fast_tanh2 do Python. Depois, você pode comparar o desempenho da função com a implementação do Python.

Siga estes passos para chamar a DLL do módulo de extensão do Python:

  1. Abra o arquivo .py para seu projeto Python no editor de código.

  2. No final do arquivo, adicione o seguinte código para chamar os métodos exportados das DLLs e mostrar sua saída:

    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. Execute o programa Python selecionando Depurar>Iniciar sem Depuração ou use o atalho de teclado Ctrl+F5.

    Observação

    Se o comando Iniciar sem depuração estiver indisponível, no Gerenciador de soluções, clique com o botão direito do mouse no projeto Python e escolha Definir como Projeto de Inicialização.

    Quando o programa é executado, observe que as rotinas C++ são executadas cerca de 5 a 20 vezes mais rápido que a implementação do Python.

    Veja este exemplo de saída típica do 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. Tente aumentar a variável COUNT para que as diferenças de tempo sejam mais evidentes.

    Uma compilação de Depuração do módulo C++ também é executada mais lentamente do que uma compilação de Versão, pois o build de Depuração é menos otimizado e contém diversas verificações de erro. Tente alternar entre essas configurações de compilação para comparação, mas lembre-se de atualizar as propriedades definidas antes para a configuração de versão.

Aborde a velocidade e a sobrecarga do processo

Na saída, você pode notar que a extensão PyBind11 não é tão rápida quanto a extensão CPython, embora deva ser mais rápida que a implementação de Python pura. O principal motivo para a diferença é por causa do uso do sinalizador METH_O. Esse sinalizador não dá suporte a vários parâmetros, nomes de parâmetros ou argumentos de palavras-chave. O PyBind11 gera um código um pouco mais complexo para fornecer uma interface mais semelhante ao Python aos chamadores. Como o código de teste chama a função 500.000 vezes, os resultados podem amplificar muito essa sobrecarga.

Você pode reduzir ainda mais a sobrecarga movendo o loop for para o código nativo Python. Essa abordagem envolve o uso do protocolo iterador (ou o tipo py::iterable PyBind11 para o parâmetro de função) para processar cada elemento. Remover as transições repetidas entre Python e C++ é uma maneira eficaz de reduzir o tempo necessário para processar a sequência.

Solucionar os problemas de importação de erros

Se você receber uma mensagem de ImportError ao tentar importar seu módulo, poderá resolvê-la de uma das seguintes maneiras:

  • Ao compilar por meio de uma referência de projeto, verifique se as propriedades do projeto C++ correspondem ao ambiente Python ativado do projeto Python. Confirme se os mesmos locais de pasta estão em uso para os arquivos Incluir (.h) e Biblioteca (DLL).

  • Confirme que o arquivo de saída esteja nomeado corretamente, como superfastcode.pyd. Um nome ou extensão incorreta evita a importação do arquivo necessário.

  • Se você instalou o módulo usando o arquivo setup.py, execute o comando pip no ambiente python ativado no seu projeto Python. Quando você expande o ambiente Python ativo para seu projeto no Gerenciador de Soluções, você deve ver uma entrada para o projeto C++, como superfastcode.

Depurar o código C++

O Visual Studio é compatível com a depuração de código de Python e C++ juntos. Os passos a seguir demonstram o processo de depuração para o projeto C++ superfastcode, mas o processo é o mesmo para o projeto superfastcode2.

  1. No Gerenciador de Soluções, clique com o botão direito do mouse no projeto Python e selecione Propriedades.

  2. No painel Propriedades, selecione a guia Depurar e a opção Depurar>Habilitar a depuração de código nativo.

    Dica

    Quando você habilitar a depuração de código nativo, a janela de Saída do Python poderá fechar imediatamente depois que o programa terminar sem pausar ou apresentar o prompt Pressione qualquer tecla para continuar. Para forçar a pausa e o prompt depois de habilitar a depuração de código nativo, adicione o argumento -i ao campo Executar>Argumentos do interpretador na guia Depurar. Esse argumento coloca o interpretador Python no modo interativo após a execução do código. O programa aguarda você selecionar Ctrl+Z+Enter para fechar a janela. Uma abordagem alternativa é adicionar as instruções import os e os.system("pause") no final do seu programa Python. Esse código duplicará o prompt de pausa original.

  3. Selecione Arquivo>Salvar (ou Ctrl+S) para salvar as alterações de propriedade.

  4. Na barra de ferramentas do Visual Studio, defina a configuração Build para Depurar.

  5. Como o código geralmente demora mais para ser executado no depurador, talvez seja interessante alterar a variável COUNT no seu arquivo de projeto Python .py para um valor que seja cerca de cinco vezes menor do que o valor padrão. Por exemplo, altere-o de 500000 para 100000.

  6. No seu código C++, defina um ponto de interrupção na primeira linha do método tanh_impl.

  7. Inicie o depurador selecionando Depurar>Iniciar depuração ou use o atalho de teclado F5.

    O depurador para quando o código do ponto de interrupção é chamado. Se o ponto de interrupção não for atingido, confirme que a configuração esteja definida como Depuração e que você tenha salvado o projeto (o que não acontece automaticamente ao iniciar o depurador).

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

  8. Neste ponto de interrupção, você poderá executar o código C++ em etapas, examinar variáveis e assim por diante. Para obter mais informações sobre esses recursos, consulte Depurar Python e C++ juntos.

Abordagens alternativas

Você pode criar extensões do Python de várias maneiras, conforme descrito na tabela a seguir. As duas primeiras linhas, CPython e PyBind11, são abordadas neste artigo.

Abordagem Vintage Usuários representantes
Módulos de extensão do C/C++ para CPython 1991 Biblioteca Padrão
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