Como usar bibliotecas feitas em C no Python

Yaks Souza, pythonc languagebindingbackend
Back

A maioria das linguagens de programação atuais são escritas em C, e o Python não é diferente, e como Ruby ou Go o Python tem uma ligação especial com a linguagem, da para usar bibliotecas do C dentro do Python, é o que chamamos de binding.

Como funciona essa "bruxaria"

Quando Guido Van Rossum idealizou o Python, ele usou o C como base de sua argumentação, já que segundo ele o C tem programas muito grandes e muitas vezes difíceis de entender. E então criou o Python para ser uma linguagem simples e de fácil compreensão, e ele conseguiu, mas com isso perdeu todas as vantagens que existem em usar o C, como acesso direto à memória e aos componentes de hardware do sistema (sem falar da performance e velocidade, porque vamos concordar que Python é uma carroça). A solução para isso foi permitir que fosse possível usar bindings de C dentro do Python, isso é possível por que todo o código que você escreve em Python é "compilado" para um binário PYC, uma espécie de bytecode do python, e para usar uma biblioteca do C junto com o PYC o interpretador usa linka abiblioteca C junto com esse bytecode, e é por isso que o interpretador cria uma pasta "pycache" sempre que tem importação de bibliotecas pro seu script python.

Mão do código!

Antes de mais nada certifique-se de que baixou os pacotes de desenvolvimento compatíveis com seu sistema, nos derivados de debian por exemplo é o pacote build-essentials, de arch é o base-devel.

A segunda coisa a fazer é o módulo, em um programa C normal ela ficaria assim:

#include <stdlib.h>
int main(int ac, char ** av){
  return system(av[0]);
}

Para transformá-lo em um módulo Python, temos que incluir o header "Python.h", que contém a API do Python e nos proverá todos os métodos, macros e tipos necessários para fazer um módulo Python em C. Depois escrevemos a função run, que vai "rodar" os comandos passados para ela através de uma string, ela vai ser a função que irá chamar a nossa função system do codigo em C. Como no Python tudo é um objeto a função retorna sempre um ponteiro para um PyObject (um objeto python), sempre recebe 2 parâmetros, o primeiro é o famoso self usado para referenciar items em uma classe Python, e o segundo é os argumentos que a função vai receber, ambos são também ponteiros de PyObject, isso vai ficar mais ou menos assim e depois nós criamos a variável cmd, que vai receber a string esperada dos argumentos (args):

#include <stdlib.h>
#include <Python.h>

PyObject * run (PyObject * self, PyObject * args){
   char * cmd;

   if(!PyArg_ParseTuple(args, "s", &cmd))
        exit (1);

   return PyLong_FromLong( system(cmd) );
}

Agora parece mais simples né? Agora temos que criar a lista de métodos que o nosso módulo vai ter:

static PyMethodDef shellMethods[] = {
    {
        "run",               // nome da função no python
         run,                // nome da função no C
         METH_VARARGS,       // diz que é um método com argumentos
         "Run shell command" // descrição da função
    },

    {NULL, NULL, 0, NULL}  // simboliza o fim da lista de funções
};

E finalmente vamos finalizar o módulo dando as suas propriedades e criando o método init dele:

static struct PyModuleDef shell = {
      PyModuleDef_HEAD_INIT,
      "shell",              // nome do módulo
      "Shell module",       // descrição
      -1,                   // nível do escopo do módulo (-1 é global)
      shellMethods          // lista com as funções do módulo
};

// método __init__
PyMODINIT_FUNC PyInit_shell (void) {
      return PyModule_Create(&shell);
}

Agora é só criar um setup.py com as configurações do módulo: from distutils.core import setup, Extension

def main():
    setup(
        author="<You Name>",
        author_email="<You Email>",
        name="shell",                                 # nome do módulo
        version="1.0.0",                              # versão
        description="Run shell commands",             # descrição
        ext_modules=[Extension("shell", ["shell.c"])] # lista de módulos
    )


if _name__ == "__main__":
    main()

Para compilar e instalar o módulo é só rodar:

$ python3 setup.py install --user

E para usar é só abrir o terminal interativo do Python e digitar:

>>> import shell
>>> shell.run("uname -a")
Linux Karen 5.4.8-zen1-1-zen #1 ZEN SMP PREEMPT Sat, 04 Jan 2020 23:38:36 +0000 x86_64 GNU/Linux
0

Para mais informações sobre o uso deste recurso maravilhoso do Python consulte a documentação oficial.

© Yaks Souzalinkedin/in/plankiton
github/plankiton