Senhoras e senhores, apresento-lhes esse casamento que sem sombra de dúvidas só tem como dar certo: C++ & Python. Neste artigo, vou explicar como estender a linguagem Python usando C++ e como instalar o mesmo usando setup.py.
Ai vocês me perguntam: Porque eu tenho que escrever trocentas linhas (eu disse trocentas linhas!) em C++ só para fazer um módulo, sendo que eu posso fazer isso com o próprio Python?
Bom, o motivo é bem simples e para terem a resposta, basta dar uma olhada nesse pequeno benchmark:
Caso de uso
Criando uma função calcular o número fatorial
Digamos que precisamos criar uma função que retorna o fatorial de um determinado número. Se usarmos um número pequeno, como 5, 20 ou 50, não haverá grandes problemas. Mas o que acontece se usarmo um número como 9999 (ou até mesmo maior)? Dependendo como você fizer ou de qual linguagem usar, você terá problemas.
É claro que estou superestimando o case, mas vamos imaginar que tratasse de um caso que necessite de alto desempenho e pouco processamento.
Então, vamos criar uma função que calcule o fatorial nas linguagens Python e C++. Depois criamos o mesmo módulo em C++ e o exportaremos para Python e compararemos os desempenhos de cada solução. Tentarei usar o melhor algoritmo possível para uma comparação justa e usarei pseudo-código para representá-la.
Função fat(número): Se número for igual a 1: Devolver número; Senão: Devolver número multiplicado por Função fat(número menos 1);
Bom, como dizia o Jack Estripador, vamos por partes! Começaremos o nosso teste implementando o algorítimo no Python.
Implementei o algoritmo para calcular o fatorial de 9999 e inclui algumas funções para fazer o cálculo do tempo de execução.
fat.py
#!/usr/bin/env python import sys import time def fat(number): if(number==1): return number return number * fat( number - 1 ) def main(): #Pega o tempo inicial t1 = time.time() #executa o fatorial fat(9999) #pega o tempo final t2 = time.time() #exibe o tempo de execucao print "tempo de execucao para calcular o "\ "fatorial, foi de: ", (t2-t1) if __name__=='__main__': sys.setrecursionlimit(999999) main()
O tempo de execução em Python foi de 0.0823390483856 segundos.
Agora faremos a mesma coisa com C++.
fat.cpp
#include <cstdlib> #include <iostream> #include <time.h> using namespace std; /** Prototype */ long double fat(double number); int main(){ // Variáveis para o calculo do tempo de execução float tempo; time_t t_inicio,t_fim; // capturando o tempo inicial t_inicio = time(NULL); fat(9999); // capturando o tempo final t_fim = time(NULL); // verificando o intervalo tempo = difftime(t_fim,t_inicio); // Exibindo o tempo de execução cout << "O tempo de execução do "; cout << "programa foi de: " << tempo << endl; return 1; } long double fat(double number){ if(number == 1) return number; return number * fat(number -1); }
Após compilarmos e executarmos o programa, o seu tempo de execução em C++ foi de 0.0736501216888 segundos.
Bom, agora que a brincadeira começa. Vamos criar um arquivo chamado PyFat.cpp, incluir a função fat e registrá-la como módulo do Python.
PyFat.cpp
#include "/usr/include/python2.7/Python.h" // função que calcula o fatorial long double fat(double number){ if(number == 1) return number; return number * fat(number -1); } /** Prototipo da função */ static PyObject *pyFat(PyObject *self, PyObject *args){ double input; // Verifica se foi passado algum argumento pelo Python if(!PyArg_ParseTuple(args,"i",&input)){ return 0; } return Py_BuildValue("i",fat(input)); } // Resgistra a função static PyMethodDef PrMethods[] = { {"fat",pyFat,METH_VARARGS,"Calcula fatorial"}, {NULL, NULL, 0, NULL} }; // Inicia a função initpr PyMODINIT_FUNC initpr(void){ (void) Py_InitModule("pyFat",PrMethods); }
Para testar, você pode compilar na mão ao invés de usar o setup.py basta fazer da seguinte forma:
root@server:~$ gcc -c PyFat.cpp -I/Python2.7/include root@server:~$ gcc -shared hellomodule.o -L/Python2.7/libs -lpython2.7 -o pyfat.so
Agora que nosso arquivo PyFat.cpp está pronto, vamos criar o nosso arquivo setup.py para instalar o nosso módulo no Python.
#!/usr/bin/env python from distutils.core import setup, Extension module = Extension('fat', sources = ['PyFat.cpp']) setup(name = 'Fatorial', version = '1.0', ext_modules = [module])
Vamos dar permissão de execução ao arquivo e realizar o seguinte comando para a instalação do nosso módulo:
./setup.py install
Pronto, o nosso módulo está pronto para usar. Que tal testarmos o tempo de execução usando o nosso módulo fat?
Vamos criar o arquivo de teste. vou chamá-lo de FatPyTest.py.
import fat import sys import time def main(): #Pega o tempo inicial t1 = time.time() #executa o fatorial fat.fat(9999) #pega o tempo final t2 = time.time() #exibe o tempo de execucao print "tempo de execucao para calcular"\ " o fatorial, foi de: ", (t2-t1) if __name__=='__main__': sys.setrecursionlimit(999999) main()
Tempo de execução usando o módulo foi de 0.0740849971771 segundos. Tivemos um ganho de 0.0082540512085 segundos.
Claro que criar um módulos em C++ só para calcular o fatorial de um número não compensa, mas se pensarmos em rotinas que requerem grande processamento, com certeza valerá apena investir nessa solução. Lembrando que não só é possível estender a linguagem Python, essa técnica vale para quase todas as linguagens.
Caso queiram fazer download dos arquivos basta acessar a seguinte url: https://github.com/kmilotxm/post-estendendo-python-com-cpp
Bom, espero que tenham gostado da dica.