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.



