web-dev-qa-db-fra.com

Est-il possible de compiler Python en code machine?

Dans quelle mesure serait-il possible de compiler Python (éventuellement via une représentation C intermédiaire) en code machine?

Vraisemblablement, il devrait être lié à une bibliothèque d'exécution Python, et toutes les parties de la bibliothèque standard Python qui étaient Python elles-mêmes devraient également être compilées (et liées).

En outre, vous devez regrouper l'interpréteur Python si vous souhaitez effectuer une évaluation dynamique des expressions, mais un sous-ensemble de Python qui ne le permet pas serait peut-être utile.

Aurait-il des avantages en termes de vitesse et/ou d'utilisation de la mémoire? Vraisemblablement, le temps de démarrage de l'interpréteur Python serait éliminé (bien que les bibliothèques partagées auraient toujours besoin d'être chargées au démarrage).

115
Andy Balaam

Essayez ShedSkin Compilateur Python-to-C++, mais il est loin d’être parfait. En outre, il existe Psyco - Python JIT si seule une accélération est nécessaire. Mais à mon humble avis, cela ne vaut pas la peine. Pour les parties de code critiques pour la vitesse, la meilleure solution serait de les écrire en tant qu’extensions C/C++. 

22
cleg

Comme @Greg Hewgill le dit, il y a de bonnes raisons pour lesquelles ce n'est pas toujours possible. Cependant, certains types de code (comme le code très algorithmique) peuvent être transformés en "vrai" code machine. 

Il y a plusieurs options:

  • Utilisez Psyco , qui émet du code machine de manière dynamique. Vous devez cependant choisir avec soin les méthodes/fonctions à convertir.
  • Utilisez Cython , qui est un langage Python -like compilé dans une extension C de Python
  • Utilisez PyPy , qui contient un traducteur de RPython (un sous-ensemble restricted de Python ne prenant pas en charge certaines des fonctionnalités les plus "dynamiques" de Python) en C ou LLVM.
    • PyPy est encore très expérimental
    • toutes les extensions ne seront pas présentes

Après cela, vous pouvez utiliser l'un des packages existants (freeze, Py2exe, PyInstaller) pour tout mettre dans un binaire.

Dans l'ensemble: il n'y a pas de réponse générale à votre question. Si le code Python est essentiel à la performance, essayez d’utiliser autant de fonctionnalités intégrées que possible (ou posez une question "Comment rendre mon code Python plus rapide"). Si cela ne vous aide pas, essayez d'identifier le code, de le transférer en C (ou Cython) et d'utiliser l'extension.

46
Torsten Marek

py2c ( http://code.google.com/p/py2c ) peut convertir le code python en c/c ++ Je suis le développeur solo de py2c.

16
Ramchandra Apte

Nuitka est un compilateur Python vers C++ qui lie contre libpython. Cela semble être un projet relativement nouveau. L'auteur affirme une amélioration de la vitesse par rapport à CPython sur le benchmark pystone. 

13
bcattle

PyPy est un projet de réimplémentation de Python en Python, utilisant la compilation en code natif comme l'une des stratégies d'implémentation (les autres étant une VM avec JIT, utilisant la JVM, etc.). Leurs versions C compilées fonctionnent en moyenne plus lentement que CPython mais beaucoup plus rapidement pour certains programmes.

Shedskin est un compilateur expérimental Python-to-C++.

Pyrex est un langage spécialement conçu pour écrire des modules d'extension Python. Il est conçu pour combler le fossé entre le monde de Python de Nice, de haut niveau et facile à utiliser, et le monde désordonné de C, de bas niveau.

13
pdc

Pyrex est un sous-ensemble du langage Python compilé en C, effectué par le gars qui a construit le premier list comprehensions pour Python. Il a été principalement développé pour les enveloppes de construction, mais peut être utilisé dans un contexte plus général. Cython est une fourche de pyrex maintenue plus activement.

10

Cela peut sembler raisonnable à première vue, cependant, il existe de nombreuses choses ordinaires dans Python qui ne sont pas directement mappables à une représentation en C sans transférer une grande partie du support d'exécution Python. On pense par exemple à la frappe de canard. De nombreuses fonctions en Python qui lisent les entrées peuvent prendre un objet fichier ou semblable à un fichier, à condition qu'il prenne en charge certaines opérations, par exemple. read () ou readline (). Si vous réfléchissez à ce qu'il faudrait pour mapper ce type de support sur C, vous commencez à imaginer exactement le genre de choses que fait déjà le système d'exécution Python.

Il existe des utilitaires tels que py2exe qui regrouperont un programme et une exécution Python dans un seul exécutable (dans la mesure du possible).

9
Greg Hewgill

Quelques références supplémentaires:

5
serge-sans-paille

Jython a un compilateur ciblant le bytecode de la machine virtuelle Java. Le bytecode est totalement dynamique, tout comme le langage Python lui-même! Très sympa. (Oui, comme le dit la réponse de Greg Hewgill, le bytecode utilise le runtime de Jython et le fichier jar de Jython doit être distribué avec votre application.)

3
Chris Jester-Young

Psyco est une sorte de compilateur juste à temps (JIT): compilateur dynamique pour Python, le code est exécuté 2 à 100 fois plus rapidement, mais il nécessite beaucoup de mémoire.

En bref: il exécute votre logiciel Python existant beaucoup plus rapidement, sans modification de votre source, mais il ne compile pas en code objet de la même manière qu'un compilateur C le ferait.

2
Pierre-Jean Coudert

La réponse est "oui, c'est possible". Vous pouvez prendre du code Python et essayer de le compiler dans le code C équivalent à l'aide de l'API CPython. En fait, il y avait un projet Python2C qui faisait exactement cela, mais je n'en avais pas entendu parler depuis de nombreuses années (dans Python 1.5 jours, c'était la dernière fois que je l'avais vu.)

Vous pouvez essayer de traduire le code Python en C natif autant que possible et de revenir à l'API CPython lorsque vous avez besoin de fonctionnalités Python réelles. Je me suis moqué de cette idée moi-même depuis un ou deux mois. Cependant, cela représente un travail énorme, et une quantité énorme de fonctionnalités Python est très difficile à traduire en C: fonctions imbriquées, générateurs, tout sauf des classes simples avec des méthodes simples, tout ce qui implique la modification de globaux de modules depuis l'extérieur du module, etc. , etc.

2
Thomas Wouters

Cela ne compile pas Python en code machine. Mais permet de créer une bibliothèque partagée pour appeler du code Python.

Si ce que vous recherchez est un moyen facile d’exécuter du code Python à partir de C sans faire appel à execp. Vous pouvez générer une bibliothèque partagée à partir de code python encapsulé avec quelques appels à API d'intégration de Python . Eh bien, l’application est une bibliothèque partagée, un fichier .so que vous pouvez utiliser dans de nombreuses autres bibliothèques/applications.

Voici un exemple simple de création d’une bibliothèque partagée, que vous pouvez relier à un programme C. La bibliothèque partagée exécute le code Python.

Le fichier python qui sera exécuté est 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

Vous pouvez l'essayer avec python2 -c "import pythoncalledfromc; pythoncalledfromc.main('HELLO'). Il produira:

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

La bibliothèque partagée sera définie comme suit par callpython.h:

#ifndef CALL_PYTHON
#define CALL_PYTHON

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

#endif

Le callpython.c associé est:

// 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();
}

Vous pouvez le compiler avec la commande suivante:

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

Créez un fichier nommé callpythonfromc.c contenant les éléments suivants:

#include "callpython.h"

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

Compilez-le et lancez:

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

Ceci est un exemple très basique. Cela peut fonctionner, mais selon la bibliothèque, il peut être encore difficile de sérialiser les structures de données C en Python et de Python en C. Les choses peuvent être quelque peu automatisées ...

Nuitka pourrait être utile.

Il y a aussi numba mais ils ne visent pas tous les deux à faire exactement ce que vous voulez. La génération d'un en-tête C à partir de code Python est possible, mais uniquement si vous indiquez comment convertir les types Python en types C ou si vous pouvez en déduire ces informations. Voir python astroid pour un analyseur Python ast.

2
amirouche
0
tav