Partilhar via


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

Neste artigo, você cria um módulo de extensão C++ para CPython para calcular uma tangente hiperbólica e chamá-la de código Python. A rotina é implementada primeiro em Python para demonstrar o ganho relativo de desempenho da implementação da mesma rotina em C++.

Módulos de código escritos em C++ (ou C) são comumente usados para estender os recursos de um interpretador Python. Existem três tipos principais de módulos de extensão:

  • Módulos aceleradores: habilite o desempenho acelerado. Como Python é uma linguagem interpretada, você pode escrever um módulo acelerador em C++ para obter maior desempenho.
  • Módulos wrapper: Exponha interfaces C/C++ existentes ao código Python ou exponha uma API mais semelhante a Python que seja fácil de usar a partir do Python.
  • Módulos de acesso ao sistema de baixo nível: crie módulos de acesso ao sistema para alcançar recursos de nível inferior do CPython tempo de execução, do sistema operacional ou do hardware subjacente.

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

  • Use as extensões padrão CPython , conforme descrito na documentação do Python.
  • Use PyBind11, que recomendamos para C++11 devido à sua simplicidade. Para garantir a compatibilidade, certifique-se de que está a trabalhar com uma das versões mais recentes do Python.

O exemplo completo para este passo a passo está disponível no GitHub em python-samples-vs-cpp-extension.

Pré-requisitos

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

    Captura de tela de uma lista de opções de desenvolvimento Python, destacando a opção de ferramentas de desenvolvimento nativo Python.

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

    Observação

    Quando você instala a carga de trabalho de ciência de dados e aplicativos analíticos , Python e a opção de ferramentas de desenvolvimento nativo Python são instaladas por padrão.

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

Criar o aplicativo Python

Siga estas etapas para criar o aplicativo Python.

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

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

  3. Insira um nome e um local do projeto e selecione Criar.

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

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

    Esse código calcula uma tangente hiperbólica sem usar a biblioteca matemática, e é o que você acelera mais tarde com extensões nativas do Python.

    Sugestão

    Escreva seu código em Python puro antes de reescrevê-lo em C++. Dessa forma, você pode verificar mais facilmente se seu código Python nativo 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 selecione o atalho de teclado Ctrl+F5.

    Uma janela de comando é aberta para mostrar a saída do programa.

  6. Na saída, observe a quantidade de tempo relatada para o processo de referência.

    Para este passo a passo, o processo de benchmark deve levar aproximadamente 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 seu computador.

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

Sugestão

Ao executar benchmarks, sempre use a opção Debug>Start without Debugging . Esse método ajuda a evitar a sobrecarga que pode incorrer quando você executa o código dentro do depurador do Visual Studio.

Criar os principais projetos C++

Siga estas etapas para criar dois projetos C++ idênticos, superfastcode e superfastcode2. Mais tarde, você usa 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 nome da solução e selecione Adicionar>Novo Projeto.

    Uma solução Visual Studio pode conter 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 digite vazio na caixa Pesquisar .

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

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

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

Certifique-se de repetir essas etapas e criar dois projetos.

Sugestão

Uma abordagem alternativa está disponível quando você tem as ferramentas de desenvolvimento nativas Python instaladas no Visual Studio. Você pode começar com o modelo Python Extension Module , que pré-conclui muitas das etapas descritas neste artigo.

Para o passo a passo neste 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 economizar tempo ao escrever suas próprias extensões.

Adicionar arquivo C++ ao projeto

Em seguida, adicione um arquivo C++ a cada projeto.

  1. No 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 Arquivo C++ (.cpp).

  3. Introduza o Nome do ficheiro como module.cpp e, em seguida, selecione Adicionar.

    Importante

    Certifique-se de que o nome do ficheiro inclui a extensão .cpp . O Visual Studio procura 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:

    Captura de tela que mostra como definir o tipo de configuração de destino para o projeto C++ no Visual Studio.

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

Certifique-se de repetir essas etapas para ambos os projetos.

Configurar propriedades do projeto

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

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

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

  2. Configure as propriedades para a compilação de depuração do módulo e, em seguida, configure as mesmas propriedades para a compilação de libertaçã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:

    Captura de tela que mostra como definir a compilação do projeto e as opções de plataforma para o arquivo C++ no Visual Studio.

    1. Para Configuração, selecione Depurar ou Liberar. (Você pode ver essas opções com o prefixo Ativo .)

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

      Observação

      Ao criar seus próprios projetos, você desejará configurar as configurações de depuração e liberação separadamente, de acordo com seus requisitos específicos de cenário. Neste exercício, você define as configurações para usar uma compilação de versão do CPython. Essa configuração desabilita alguns recursos de depuração do Microsoft Visual C++ Runtime, incluindo asserções. Usar binários de depuração CPython (python_d.exe) requer configurações diferentes.

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

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

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

      Separador e secção Propriedade Valor
      Propriedades> de configuraçãoGeral Nome do destino Especifique o nome do módulo para se referir a ele a partir do Python em from...import instruções, como superfastcode. Você usa esse mesmo nome no código C++ quando define 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çãoAvançado Extensão do arquivo de destino .pyd (Módulo de Extensão Python)
      C/C++>Geral Diretórios de Inclusão Adicionais Adicione a pasta Python include conforme apropriado para sua instalação (por exemplo, c:\Python36\include).
      C/C++>Pré-processador Definições do pré-processador Se estiver presente, altere o valor _DEBUG para NDEBUG para corresponder à versão não debug do CPython. Quando utilizar python_d.exe, deixe este valor inalterado.
      C/C++>Geração de código Biblioteca de execução DLL multi-threaded (/MD) para corresponder à versão de lançamento (sem depuração) do CPython. Quando utilizar python_d.exe, deixe este valor permanecendo como Multi-threaded Debug DLL (/MDd).
      Verificações básicas de tempo de execução Predefinição
      Linker>Geral Diretórios de bibliotecas adicionais Adicione a pasta Python libs que contém arquivos .lib , conforme apropriado para sua instalação (por exemplo, c:\Python36\libs). Certifique-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 uma opção para as propriedades do projeto, o projeto não conterá nenhum arquivo de código que o Visual Studio identifique como arquivos de origem C/C++. Essa condição pode 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 criou o arquivo C++, o Visual Studio cria o arquivo, mas não define o tipo de arquivo como compilador C/C+. Esse tipo de arquivo é necessário para ativar a presença da guia de propriedades C/C++ na caixa de diálogo de propriedades do projeto. A identificação incorreta permanece mesmo se você renomear o arquivo de código com uma extensão de arquivo .cpp .

      Para definir o tipo de arquivo de código corretamente, no 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 as etapas para a outra configuração de compilação.

  3. Teste sua configuração atual. Repita as etapas a seguir para as compilações de depuração e versão de ambos os projetos C++.

    1. Na barra de ferramentas do Visual Studio, defina a configuração Build como Debug ou Release:

      Captura de tela que mostra como definir a configuração de compilação para o projeto C++ no 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 na pasta da solução , em Debug and Release, e não na pasta do projeto C++ em si.

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 o seguinte 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 seu código está correto.

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

Converter projetos C++ em extensões Python

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

As seções a seguir demonstram 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 obter mais informações sobre o código apresentado nesta seção, consulte Manual de referência da API Python/C, especialmente a página Module Objects . Ao revisar o conteúdo de referência, certifique-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 o código do tanh_impl método 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. No 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 em seu código Python, especificamente quando você usa a from...import instrução.

    O nome importado neste código deve corresponder ao valor nas propriedades do projeto em Configuration Properties>General>Target Name.

    No exemplo a seguir, o "superfastcode" nome significa que você pode usar a from superfastcode import fast_tanh instrução em Python porque fast_tanh é definido dentro de superfastcode_methods. Os nomes de arquivo que são internos ao projeto C++, como module.cpp, são inconsequentes.

    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 quando carrega o módulo. O nome do método deve ser PyInit_<module-name>, onde <module-name> corresponde exatamente à propriedade Configuration Properties>General>Target Name 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. Crie o projeto C++ e verifique seu código. Se você encontrar erros, consulte a seção "Solucionar problemas de erros de compilação".

Use PyBind11

Se concluir as etapas na seção anterior para o projeto superfastcode, poderá notar que o exercício requer código padrão para criar as estruturas de módulo para extensões C++ CPython. Neste exercício, você descobre que o PyBind11 simplifica o processo de codificação. Você usa macros em um arquivo de cabeçalho C++ para obter o mesmo resultado, mas com muito menos código. No entanto, etapas adicionais são necessárias 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 do Developer PowerShell .

  1. Abra a janela Tools>Command Line>Developer PowerShell .

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

    Visual Studio instala PyBind11 e seus pacotes dependentes.

Adicionar caminhos PyBind11 ao projeto

Após a instalação do PyBind11, você precisa adicionar os caminhos PyBind11 à propriedade Additional Include Directories do projeto.

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

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

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

    Captura de tela que mostra como realçar e copiar a lista de caminhos da janela do Developer PowerShell no Visual Studio.

    A lista de caminhos concatenados é adicionada à á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 propriedades , para o campo Configuração , selecione Liberar. (Você pode ver essa opção com o prefixo Ativo .)

  5. Na caixa de diálogo, na guia C/C++>General , expanda o menu suspenso da propriedade Additional Include Directories e selecione Editar.

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

    Repita estas etapas para cada caminho na lista concatenada copiada da janela do Developer PowerShell :

    1. Selecione Nova linha (pasta com símbolo de adição) na barra de ferramentas da caixa de diálogo pop-up.

      Captura de tela que mostra como adicionar um caminho PyBind11 à propriedade Additional Include Directories.

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

    2. Cole o caminho PyBind11 na linha vazia.

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

      Importante

      • Se o caminho contiver o prefixo -I , remova-o do caminho.
      • Para que o Visual Studio reconheça 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 Property Pages , passe o mouse sobre o valor da propriedade Additional Include Directories e confirme se os caminhos PyBind11 estão presentes.

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

Atualizar o arquivo module.cpp

A última etapa é adicionar o arquivo de cabeçalho PyBind11 e o código de macro ao arquivo C++ do projeto.

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

  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 de module.cpp , adicione código para a PYBIND11_MODULE macro 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. Crie o projeto C++ e verifique seu código. Se você encontrar erros, consulte a próxima seção, Solucionar problemas de erros de compilação.

Solucionar problemas de erros de compilação

Analise as seções a seguir para verificar possíveis problemas que podem fazer com que a compilação do módulo C++ falhe.

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

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

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

  • Para o projeto superfastcode , verifique se a propriedade do projeto C/C++>General>Additional Include Directories contém o caminho para a pasta include para sua instalação do Python. Analise 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 include para sua instalação PyBind11. Analise os passos para adicionar caminhos do PyBind ao projeto.

Para 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 Python

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 Linker>General>Additional Library Directories contém o caminho para a pasta libs para sua instalação do Python. Analise as etapas em Configurar propriedades do projeto.

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

Visual Studio relata erros de vinculador relacionados à configuração de 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, altere a configuração de destino do projeto C++ para x64.

Teste o código e compare os resultados

Agora que você tem as DLLs estruturadas como extensões Python, você pode consultá-las a partir do projeto Python, importar os módulos e usar seus métodos.

Disponibilize sua DLL para Python

Você pode disponibilizar sua DLL para Python de várias maneiras. Aqui estão duas opções a considerar:

Se seu projeto Python e projeto C++ estiverem na mesma solução, você pode usar a seguinte abordagem:

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

    Certifique-se de fazer 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.

    Captura de tela que mostra como adicionar uma referência ao projeto de código super rápido no Visual Studio.

Uma abordagem alternativa é instalar o módulo de extensão C++ em seu ambiente Python. Este método torna o módulo disponível para outros projetos Python. Para obter mais informações, consulte a documentação do projeto setuptools.

Conclua as seguintes etapas 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 em seu projeto C++ e selecione Adicionar>Novo Item.

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

  3. Introduza o Nome do ficheiro como setup.py e, em seguida, selecione Adicionar.

    Certifique-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ódigos.

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

    • Extensões 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 criar a extensão, clique com o botão direito do mouse no nome do arquivo pyproject.toml na guia da janela de código e selecione Copiar caminho completo.

    Captura de tela que mostra como copiar o caminho completo para o arquivo toml do projeto py no Visual Studio.

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

  7. No Explorador de Soluções, expanda o nó Ambientes Python para a solução.

  8. Clique com o botão direito do mouse 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á listado neste painel.

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

    Captura de tela que mostra como desinstalar um pacote no painel Ambientes Python.

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

    Captura de tela que mostra como inserir o caminho no painel Ambientes Python para instalar o módulo de extensão.

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

    Sugestão

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

Chame a DLL a partir do Python

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

Siga estas etapas para chamar a DLL do módulo de extensão em 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 exibir 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 não estiver disponível, no Gerenciador de Soluções, clique com o botão direito do mouse no projeto Python e selecione Definir como Projeto de Inicialização.

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

    Aqui está um 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 COUNT variável para que as diferenças de tempo sejam mais pronunciadas.

    Uma compilação de depuração do módulo C++ também é executada mais lentamente do que uma compilação de lançamento porque a compilação de depuração é menos otimizada e contém várias verificações de erro. Tente alternar entre as configurações de compilação para comparação, mas lembre-se de atualizar as propriedades definidas anteriormente para a configuração de versão.

Reduza 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 do que a implementação Python pura. A principal razão para a diferença é por causa do uso da bandeira METH_O. Este sinalizador não suporta 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 parecida com Python para chamadores. Como o código de teste chama a função 500.000 vezes, os resultados podem amplificar significativamente a sobrecarga.

Você pode reduzir ainda mais a sobrecarga movendo o for loop para o código Python nativo. Essa abordagem envolve o uso do protocolo iterador (ou do tipo PyBind11 py::iterable 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 erros de importação

Se receber uma ImportError mensagem quando tentar importar o módulo, pode resolvê-la de uma das seguintes formas:

  • Quando você cria através de uma referência de projeto, certifique-se de que as propriedades do seu projeto C++ correspondam ao ambiente Python ativado para seu projeto Python. Confirme se os mesmos locais de pasta estão em uso para os arquivos Include (.h) e Library (DLL).

  • Verifique se o arquivo de saída está nomeado corretamente, como superfastcode.pyd. Um nome ou extensão incorreto impede a importação do arquivo necessário.

  • Se você instalar seu módulo usando o arquivo setup.py , certifique-se de executar o pip comando no ambiente Python ativado para 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 código C++

O Visual Studio oferece suporte à depuração de código Python e C++ juntos. As etapas 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, em seguida, selecione a opção Depurar>Habilitar depuração de código nativo .

    Sugestão

    Quando você ativa a depuração de código nativo, a janela de saída do Python pode fechar imediatamente após a conclusão do programa sem pausar e mostrando o prompt Pressione qualquer tecla para continuar . Para forçar a pausa e o prompt após ativar a depuração de código nativo, adicione o -i argumento para o campo Executar>Argumentos do Interpretador na guia Depurar. Este argumento coloca o interpretador Python no modo interativo após a execução do código. O programa espera que você selecione Ctrl+Z+Enter para fechar a janela. Uma abordagem alternativa é adicionar instruções import os e os.system("pause") no final do seu programa Python. Esse código duplica o prompt de pausa original.

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

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

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

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

  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 é executado. Se o ponto de interrupção não for atingido, verifique se a configuração está definida como Depurar e se você salvou o projeto, o que não acontece automaticamente quando você inicia o depurador.

    Captura de tela do código C++ que contém um ponto de interrupção no Visual Studio.

  8. No ponto de interrupção, você pode percorrer o código C++, 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 Python de várias maneiras, conforme descrito na tabela a seguir. As duas primeiras linhas CPython e PyBind11, são discutidas neste artigo.

Abordagem Vintage Utilizadores representativos
Módulos de extensão C/C++ para CPython 1991 Biblioteca Standard
PyBind11 (recomendado para C++) 2015
Cython (recomendado para C) 2007 Gevent, Kivy
HPy 2019
mypyc 2017
tipos 2003 OSPrip
CFFI 2013 Criptografia, Pypy
SWIG 1996 CRFSUITE
Boost.Python 2002
cppyy 2017