Est-il possible de compiler Python en code machine?

128

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 elles-mêmes Python devraient également être compilées (et liées).

De plus, vous auriez besoin de regrouper l'interpréteur Python si vous vouliez faire une évaluation dynamique des expressions, mais peut-être qu'un sous-ensemble de Python qui ne le permettait pas serait toujours utile.

Fournirait-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 encore besoin d'être chargées au démarrage).

Andy Balaam
la source
2
Btw, votre question serait à mon humble avis plus claire si vous demandiez un "code machine" plutôt qu'un code objet.
Torsten Marek

Réponses:

31

Essayez le compilateur ShedSkin Python-to-C ++, mais il est loin d'être parfait. Il y a aussi 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 sous forme d'extensions C / C ++.

cleg
la source
5
Pour info, ShedSkin a abandonné la prise en charge de Windows.
sorin le
2
@sorin: eh bien, aujourd'hui, il prend en charge Windows ... code.google.com/p/shedskin/downloads
2
La meilleure solution, en termes de vitesse, pourrait encore être PyPy .
Cees Timmerman
shedskin n'a eu aucun travail dessus depuis environ deux ans maintenant. :(
Perkins
53

Comme le dit @Greg Hewgill, 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 code machine "réel".

Il existe plusieurs options:

  • Utilisez Psyco , qui émet du code machine de manière dynamique. Vous devez cependant choisir soigneusement les méthodes / fonctions à convertir.
  • Utilisez Cython , qui est un langage de type Python qui est compilé dans une extension Python C
  • Utilisez PyPy , qui a un traducteur de RPython (un sous - ensemble restreint de Python qui ne prend pas en charge certaines des fonctionnalités les plus «dynamiques» de Python) vers 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 seul binaire.

Dans l'ensemble: il n'y a pas de réponse générale à votre question. Si votre code Python est critique pour les performances, 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 résout pas le problème, essayez d'identifier le code et portez-le vers C (ou Cython) et utilisez l'extension.

Torsten Marek
la source
3
Pypy est le successeur de Psyco
bcattle
19

py2c ( https://github.com/pradyun/Py2C ) peut convertir du code python en c / c ++ Je suis le développeur solo de py2c.

Ramchandra Apte
la source
Cela ressemble à un outil utile. Est-il toujours maintenu?
Anderson Green
@AndersonGreen C'est à un stade précoce de développement la dernière fois que je travaillais dessus (probablement similaire maintenant). J'ai quitté le projet parce que je suis paresseux. Si vous n'avez pas remarqué le texte "Important", il est maintenant déplacé vers GitHub.
Ramchandra Apte
Le lien pointe vers unvanquished-installer , qui semble être un projet différent. Py2c est-il toujours disponible sur GitHub?
Anderson Green
@AndersonGreen Wow qui est resté inaperçu pendant si longtemps! Ici vous allez.
Ramchandra Apte
Le lien sur code.google.com/p/py2c pointe toujours vers unvanquished-installer, il doit donc être mis à jour maintenant.
Anderson Green
15

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 JVM, etc.). Leurs versions C compilées fonctionnent plus lentement que CPython en moyenne, mais beaucoup plus rapidement pour certains programmes.

Shedskin est un compilateur expérimental Python vers 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 agréable, de haut niveau et facile à utiliser de Python et le monde désordonné et de bas niveau de C.

PDC
la source
3
Cython est le fork convivial de Pyrex le plus largement utilisé et le plus développé.
Mike Graham
"le monde agréable, de haut niveau et facile à utiliser de Python et le monde désordonné et de bas niveau de C" - drôle Je pensais juste à quel point C et assembleur sont "gentils" et simples, et Python vit dans le " messy "," high-level "world
Reversed Engineer
14

Nuitka est un compilateur Python vers C ++ qui établit des liens avec libpython. Il semble que ce soit un projet relativement nouveau. L'auteur revendique une amélioration de la vitesse par rapport à CPython sur le benchmark pystone.

bovins
la source
10

Cela peut sembler raisonnable à première vue, mais il y a beaucoup de choses ordinaires en Python qui ne sont pas directement mappables à une représentation C sans transporter une grande partie du support d'exécution Python. Par exemple, la frappe de canard vient à l'esprit. De nombreuses fonctions en Python qui lisent des entrées peuvent prendre un fichier ou un objet semblable à un fichier , tant qu'il prend en charge certaines opérations, par exemple. read () ou readline (). Si vous pensez à ce qu'il faudrait pour mapper ce type de support à C, vous commencez à imaginer exactement le genre de choses que le système d'exécution Python fait déjà.

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

Greg Hewgill
la source
1
Et si mon objectif était de m'assurer que le code se compile, car les langages compilés statiquement sont (du moins à mon avis) moins susceptibles d'exploser au moment de l'exécution? Est-il possible de déterminer qu'une foo.xexpression ne fonctionnera pas car fooelle ne l'aura pas xau moment où elle est appelée. Existe-t-il des vérificateurs de code statiques pour Python? Python peut être compilé en un assembly .Net ...
Hamish Grubijan
10

Pyrex est un sous-ensemble du langage Python qui se compile en C, réalisé par le type qui a d'abord construit des compréhensions de liste pour Python. Il a été principalement développé pour la construction de wrappers mais peut être utilisé dans un contexte plus général. Cython est un fork de pyrex plus activement maintenu.

ConcernedOfTunbridgeWells
la source
2
Cython est le fork convivial de Pyrex le plus largement utilisé et le plus développé.
Mike Graham
3

Jython a un compilateur ciblant le bytecode JVM. Le bytecode est entièrement dynamique, tout comme le langage Python lui-même! Très cool. (Oui, comme l'indique la réponse de Greg Hewgill, le bytecode utilise le runtime Jython, et le fichier jar Jython doit donc être distribué avec votre application.)

Chris Jester-Young
la source
2

Psyco est une sorte de compilateur juste à temps (JIT): un compilateur dynamique pour Python, exécute le code 2 à 100 fois plus vite, mais il a besoin de beaucoup de mémoire.

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

Pierre-Jean Coudert
la source
2

La réponse est "Oui, c'est possible". Vous pouvez prendre du code Python et tenter 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 ai pas entendu parler depuis de nombreuses années (dans le Python 1.5 jours, c'est la dernière fois que je l'ai vu.)

Vous pouvez essayer de traduire le code Python en C natif autant que possible et revenir à l'API CPython lorsque vous avez besoin de fonctionnalités Python réelles. J'ai moi-même joué avec cette idée depuis un mois ou deux. Cependant, c'est énormément de travail, et une énorme quantité 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 des globaux de module depuis l'extérieur du module, etc. , etc.

Thomas Wouters
la source
2

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 simple d'exécuter du code Python à partir de C sans vous fier à des éléments execp. Vous pouvez générer une bibliothèque partagée à partir de code python encapsulé avec quelques appels à l' API d'intégration Python . Eh bien, l'application est une bibliothèque partagée, un .so que vous pouvez utiliser dans de nombreuses autres bibliothèques / applications.

Voici un exemple simple qui crée une bibliothèque partagée, que vous pouvez lier avec 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 par ce qui suit callpython.h:

#ifndef CALL_PYTHON
#define CALL_PYTHON

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

#endif

L'associé callpython.cest:

// 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.cqui contient les éléments suivants:

#include "callpython.h"

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

Compilez-le et exécutez:

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 vers Python et de Python vers 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 spécifiez comment convertir les types Python en types C ou si vous pouvez déduire ces informations. Voir python astroid pour un analyseur Python ast.

Amirouche
la source