É possível compilar Python para código de máquina?

128

Quão viável seria compilar Python (possivelmente por meio de uma representação C intermediária) em código de máquina?

Presumivelmente, seria necessário vincular a uma biblioteca de tempo de execução Python, e qualquer parte da biblioteca padrão do Python que fosse o próprio Python precisaria ser compilada (e vinculada) também.

Além disso, você precisaria agrupar o interpretador Python se desejasse fazer uma avaliação dinâmica de expressões, mas talvez um subconjunto de Python que não permitisse isso ainda fosse útil.

Isso proporcionaria vantagens de velocidade e / ou uso de memória? Presumivelmente, o tempo de inicialização do interpretador Python seria eliminado (embora as bibliotecas compartilhadas ainda precisem ser carregadas na inicialização).

Andy Balaam
fonte
2
Aliás, sua pergunta seria mais clara se você pedisse "código de máquina" em vez de código de objeto.
Torsten Marek
Experimente o transpiler Python → 11l → C ++ .
quer

Respostas:

31

Experimente o compilador ShedSkin Python para C ++, mas está longe de ser perfeito. Também existe o Psyco - Python JIT se apenas a aceleração for necessária. Mas IMHO isso não vale a pena o esforço. Para partes críticas de velocidade do código, a melhor solução seria escrevê-las como extensões C / C ++.

cleg
fonte
5
Para sua informação, o ShedSkin retirou o suporte do Windows.
21810
2
@sorin: bem, hoje ele suporta windows ... code.google.com/p/shedskin/downloads/…
2
A melhor solução, em velocidade, ainda pode ser o PyPy .
Cees Timmerman
shedskin não faz nenhum trabalho há cerca de dois anos. :( #
Perkins
53

Como o @Greg Hewgill diz, há boas razões para que isso nem sempre seja possível. No entanto, certos tipos de código (como código muito algorítmico) podem ser transformados em código de máquina "real".

Existem várias opções:

  • Use Psyco , que emite código de máquina dinamicamente. Você deve escolher cuidadosamente quais métodos / funções converter, no entanto.
  • Use Cython , que é uma linguagem semelhante ao Python, compilada em uma extensão Python C
  • Use o PyPy , que possui um tradutor do RPython (um subconjunto restrito do Python que não suporta alguns dos recursos mais "dinâmicos" do Python) para C ou LLVM.
    • PyPy ainda é altamente experimental
    • nem todas as extensões estarão presentes

Depois disso, você pode usar um dos pacotes existentes (freeze, Py2exe, PyInstaller) para colocar tudo em um binário.

Em suma: não há resposta geral para sua pergunta. Se você tiver um código Python crítico para o desempenho, tente usar o máximo possível de funcionalidades internas (ou faça uma pergunta "Como faço para tornar meu código Python mais rápido"). Se isso não ajudar, tente identificar o código e portá-lo para C (ou Cython) e use a extensão.

Torsten Marek
fonte
3
PyPy é o sucessor do Psyco
bcattle
19

py2c ( https://github.com/pradyun/Py2C ) pode converter código python em c / c ++ Eu sou o desenvolvedor solo do py2c.

Ramchandra Apte
fonte
Parece uma ferramenta útil. Ainda está sendo mantido?
Anderson Green
@AndersonGreen Está em um estágio inicial de desenvolvimento na última vez em que trabalhei nele (provavelmente semelhante agora). Eu deixei o projeto porque sou preguiçoso. Se você não notou o texto "Importante", ele foi movido para o GitHub agora.
Ramchandra Apte
O link aponta para o instalador não autorizado , que parece ser um projeto diferente. O py2c ainda está disponível no GitHub?
Anderson Green
@AndersonGreen Wow que passou despercebida por tanto tempo! Aqui você vai.
Ramchandra Apte
O link em code.google.com/p/py2c ainda aponta para o instalador não autorizado , portanto, ele precisa ser atualizado agora.
Anderson Green
15

O PyPy é um projeto para reimplementar o Python no Python, usando a compilação para código nativo como uma das estratégias de implementação (outras sendo uma VM com JIT, usando JVM, etc.). Suas versões C compiladas são mais lentas que o CPython, em média, mas muito mais rápidas para alguns programas.

Shedskin é um compilador Python para C ++ experimental.

Pyrex é uma linguagem especialmente projetada para escrever módulos de extensão Python. Ele foi projetado para preencher a lacuna entre o mundo agradável, de alto nível e fácil de usar do Python, e o mundo confuso e de baixo nível do C.

pdc
fonte
3
Cython é o fork amigável do Pyrex, mais amplamente utilizado e desenvolvido de forma mais ativa.
Mike Graham
"o mundo agradável, de alto nível e fácil de usar do Python, e o mundo bagunçado e de baixo nível do C" - engraçado, eu estava pensando em como C e assembler são "agradáveis" e simples, e o Python vive no " mundo bagunçado "," de alto nível "
Reversed Engineer
10

Isso pode parecer razoável à primeira vista, no entanto, existem muitas coisas comuns no Python que não são diretamente mapeadas para uma representação C sem carregar muito do suporte ao tempo de execução do Python. Por exemplo, digitar patos vem à mente. Muitas funções no Python que lêem entrada podem levar um arquivo ou objeto semelhante a um arquivo , desde que ele suporte certas operações, por exemplo. read () ou readline (). Se você pensar no que seria necessário para mapear esse tipo de suporte para C, você começará a imaginar exatamente o tipo de coisa que o sistema de tempo de execução Python já faz.

Existem utilitários como o py2exe que agrupam um programa Python e o tempo de execução em um único executável (na medida do possível).

Greg Hewgill
fonte
1
E se meu objetivo fosse garantir que o código seja compilado, porque as linguagens estaticamente compiladas são (pelo menos na minha opinião) menos propensas a explodir em tempo de execução? É possível determinar que alguma foo.xexpressão não funcionará porque foonão terá xno momento em que é chamada. Existem verificadores de código estático para Python? Python pode ser compilado para um .NET assembly ...
Hamish Grubijan
10

Pyrex é um subconjunto da linguagem Python que é compilado em C, feito pelo cara que primeiro criou as compreensões de lista para Python. Foi desenvolvido principalmente para a construção de invólucros, mas pode ser usado em um contexto mais geral. O Cython é um garfo de pirex mais bem mantido.

ConcernedOfTunbridgeWells
fonte
2
Cython é o fork amigável do Pyrex, mais amplamente utilizado e desenvolvido de forma mais ativa.
Mike Graham
3

O Jython possui um compilador direcionado ao bytecode da JVM. O bytecode é totalmente dinâmico, assim como a própria linguagem Python! Muito legal. (Sim, como alude a resposta de Greg Hewgill, o bytecode usa o tempo de execução Jython e, portanto, o arquivo jar Jython deve ser distribuído com seu aplicativo.)

Chris Jester-Young
fonte
2

Psyco é um tipo de compilador just-in-time (JIT): compilador dinâmico para Python, executa código 2-100 vezes mais rápido, mas precisa de muita memória.

Resumindo: ele executa o software Python existente muito mais rápido, sem alterações na fonte, mas não compila para codificar objetos da mesma maneira que um compilador C faria.

Pierre-Jean Coudert
fonte
2

A resposta é "Sim, é possível". Você pode pegar o código Python e tentar compilá-lo no código C equivalente usando a API CPython. De fato, costumava haver um projeto Python2C que fazia exatamente isso, mas eu não ouvia falar disso há muitos anos (nos 1,5 dias do Python foi quando eu o vi pela última vez).

Você pode tentar converter o código Python em C nativo o máximo possível e voltar à API do CPython quando precisar de recursos reais do Python. Estou brincando com essa ideia nos últimos dois meses. É, no entanto, uma enorme quantidade de trabalho, e uma enorme quantidade de recursos do Python é muito difícil de traduzir em funções C: aninhadas, geradores, tudo menos classes simples com métodos simples, qualquer coisa que envolva a modificação de módulos globais fora do módulo, etc. etc.

Thomas Wouters
fonte
2

Isso não compila Python para código de máquina. Mas permite criar uma biblioteca compartilhada para chamar o código Python.

Se o que você está procurando é uma maneira fácil de executar o código Python a partir do C sem depender do material execp. Você pode gerar uma biblioteca compartilhada a partir do código python agrupado com algumas chamadas para a API de incorporação do Python . Bem, o aplicativo é uma biblioteca compartilhada, um arquivo .so que você pode usar em muitas outras bibliotecas / aplicativos.

Aqui está um exemplo simples que cria uma biblioteca compartilhada, que você pode vincular a um programa C. A biblioteca compartilhada executa o código Python.

O arquivo python que será executado é pythoncalledfromc.py:

# -*- encoding:utf-8 -*-
# this file must be named "pythoncalledfrom.py"

def main(string):  # args must a string
    print "python is called from c"
    print "string sent by «c» code is:"
    print string
    print "end of «c» code input"
    return 0xc0c4  # return something

Você pode experimentá-lo python2 -c "import pythoncalledfromc; pythoncalledfromc.main('HELLO'). Ele produzirá:

python is called from c
string sent by «c» code is:
HELLO
end of «c» code input

A biblioteca compartilhada será definida pelo seguinte callpython.h:

#ifndef CALL_PYTHON
#define CALL_PYTHON

void callpython_init(void);
int callpython(char ** arguments);
void callpython_finalize(void);

#endif

O associado callpython.cé:

// gcc `python2.7-config --ldflags` `python2.7-config --cflags` callpython.c -lpython2.7 -shared -fPIC -o callpython.so

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <python2.7/Python.h>

#include "callpython.h"

#define PYTHON_EXEC_STRING_LENGTH 52
#define PYTHON_EXEC_STRING "import pythoncalledfromc; pythoncalledfromc.main(\"%s\")"


void callpython_init(void) {
     Py_Initialize();
}

int callpython(char ** arguments) {
  int arguments_string_size = (int) strlen(*arguments);
  char * python_script_to_execute = malloc(arguments_string_size + PYTHON_EXEC_STRING_LENGTH);
  PyObject *__main__, *locals;
  PyObject * result = NULL;

  if (python_script_to_execute == NULL)
    return -1;

  __main__ = PyImport_AddModule("__main__");
  if (__main__ == NULL)
    return -1;

  locals = PyModule_GetDict(__main__);

  sprintf(python_script_to_execute, PYTHON_EXEC_STRING, *arguments);
  result = PyRun_String(python_script_to_execute, Py_file_input, locals, locals);
  if(result == NULL)
    return -1;
  return 0;
}

void callpython_finalize(void) {
  Py_Finalize();
}

Você pode compilá-lo com o seguinte comando:

gcc `python2.7-config --ldflags` `python2.7-config --cflags` callpython.c -lpython2.7 -shared -fPIC -o callpython.so

Crie um arquivo nomeado callpythonfromc.cque contenha o seguinte:

#include "callpython.h"

int main(void) {
  char * example = "HELLO";
  callpython_init();
  callpython(&example);
  callpython_finalize();
  return 0;
}

Compile e execute:

gcc callpythonfromc.c callpython.so -o callpythonfromc
PYTHONPATH=`pwd` LD_LIBRARY_PATH=`pwd` ./callpythonfromc

Este é um exemplo muito básico. Pode funcionar, mas, dependendo da biblioteca, ainda pode ser difícil serializar estruturas de dados C para Python e de Python para C. As coisas podem ser um pouco automatizadas ...

Nuitka pode ser útil.

Também existe o numba, mas ambos não pretendem fazer exatamente o que você deseja. É possível gerar um cabeçalho C a partir do código Python, mas apenas se você especificar como converter os tipos Python em tipos C ou se puder inferir essas informações. Veja python astroid para um analisador Python ast.

amirouche
fonte