web-dev-qa-db-fra.com

Quelle est exactement Python Bytecode exécuté dans CPython?

J'essaie de comprendre comment Python fonctionne (parce que je l'utilise tout le temps!). À ma connaissance, lorsque vous exécutez quelque chose comme python script.py, le script est converti en bytecode puis l'interpréteur/VM/CPython - vraiment juste un programme C - lit dans le python bytecode et exécute le programme en conséquence.

Comment ce bytecode est-il lu? Est-ce similaire à la façon dont un fichier texte est lu en C? Je ne sais pas comment le code Python est converti en code machine. Est-ce le cas que l'interpréteur Python (la commande python dans la CLI) est vraiment juste un programme C précompilé qui est déjà converti en code machine, puis le python les fichiers de bytecode sont simplement passés par ce programme? En d'autres termes, mon programme Python n'est-il jamais réellement converti en code machine? L'interpréteur python est-il déjà dans le code machine, donc mon script ne doit jamais l'être?

50
mergesort

Oui, votre compréhension est correcte. Il y a fondamentalement (très fondamentalement) une instruction switch géante à l'intérieur de l'interpréteur CPython qui dit "si l'opcode actuel est tel ou tel, faites ceci et cela".

http://hg.python.org/cpython/file/3.3/Python/ceval.c#l79

D'autres implémentations, comme Pypy, ont une compilation JIT, c'est-à-dire qu'elles traduisent Python en codes machine à la volée).

29
georg

Si vous voulez voir le bytecode d'un code (que ce soit du code source, un objet de fonction en direct ou un objet de code, etc.), le module dis vous dira exactement ce dont vous avez besoin. Par exemple:

>>> dis.dis('i/3')
  1           0 LOAD_NAME                0 (i)
              3 LOAD_CONST               0 (3)
              6 BINARY_TRUE_DIVIDE
              7 RETURN_VALUE

Les documents dis expliquent ce que signifie chaque bytecode. Par exemple, LOAD_NAME :

Pousse la valeur associée à co_names[namei] sur la pile.

Pour comprendre cela, vous devez savoir que l'interpréteur de bytecode est un virtuel stack machine , et ce co_names est. Les documents du module inspect ont un joli tableau montrant les attributs les plus importants des objets internes les plus importants, vous pouvez donc voir que co_names est un attribut d'objets code qui contient un Tuple de noms de variables locales. En d'autres termes, LOAD_NAME 0 pousse la valeur associée à la 0e variable locale (et dis la recherche utilement et voit que la 0e variable locale est nommée 'i').

Et cela suffit pour voir qu'une chaîne de bytecodes ne suffit pas; l'interpréteur a également besoin des autres attributs de l'objet code et, dans certains cas, des attributs de l'objet fonction (qui est également l'origine des environnements locaux et globaux).

Le module inspect dispose également de quelques outils qui peuvent vous aider davantage à étudier le code en direct.

C'est suffisant pour comprendre beaucoup de choses intéressantes. Par exemple, vous savez probablement que Python détermine au moment de la compilation si une variable dans une fonction est locale, fermée ou globale, selon que vous lui affectez n'importe où dans le corps de la fonction (et sur n'importe quelle instruction nonlocal ou global); si vous écrivez trois fonctions différentes et comparez leur désassemblage (et les autres attributs pertinents), vous pouvez assez facilement comprendre exactement ce qu'il doit faire.

(Le seul élément délicat ici est de comprendre les cellules de fermeture. Pour vraiment obtenir cela, vous devrez avoir 3 niveaux de fonctions, pour voir comment celui du milieu fait avancer les choses pour le plus intérieur.)


Pour comprendre comment le bytecode est interprété et comment la machine de pile fonctionne (en CPython), vous devez regarder le ceval.c code source. Les réponses de thy435 et eyquem couvrent déjà cela.


Comprendre comment les fichiers pyc sont lus prend un peu plus d'informations. Ned Batchelder a un excellent article de blog (bien que légèrement obsolète) appelé La structure des fichiers .pyc , qui couvre toutes les parties délicates et peu documentées. (Notez qu'en 3.3, une partie du code sanglant lié à l'importation a été déplacé de C vers Python, ce qui le rend beaucoup plus facile à suivre.) Mais en gros, ce ne sont que quelques informations d'en-tête et l'objet code du module, sérialisé par marshal .


Pour comprendre comment la source est compilée en bytecode, c'est la partie amusante.

Conception du compilateur CPython explique comment tout fonctionne. (Certaines des autres sections du Guide du développeur Python sont également utiles.)

Pour les premiers trucs - tokenisation et analyse - vous pouvez simplement utiliser le module ast pour aller directement au point où il est temps de faire la compilation proprement dite. Voir alors compile.c pour savoir comment cela AST est transformé en bytecode.

Les macros peuvent être un peu difficiles à travailler, mais une fois que vous avez compris comment le compilateur utilise une pile pour descendre en blocs et comment il utilise ces compiler_addop et des amis pour émettre des bytecodes au niveau actuel, tout cela a du sens.

Une chose qui surprend la plupart des gens au début est le fonctionnement des fonctions. Le corps de la définition de fonction est compilé dans un objet de code. Ensuite, la définition de la fonction elle-même est compilée en code (à l'intérieur du corps de la fonction englobante, du module, etc.) qui, lorsqu'elle est exécutée, construit un objet fonction à partir de cet objet code. (Une fois que vous pensez à la façon dont les fermetures doivent fonctionner, il est évident pourquoi cela fonctionne de cette façon. Chaque instance de la fermeture est un objet fonction distinct avec le même objet de code.)


Et maintenant, vous êtes prêt à commencer à patcher CPython pour ajouter vos propres instructions, non? Eh bien, comme le montre Changer la grammaire de CPython , il y a beaucoup de choses à faire (et encore plus si vous avez besoin de créer de nouveaux opcodes). Vous pourriez trouver plus facile d'apprendre PyPy ainsi que CPython, et commencez à pirater PyPy en premier, et ne revenez à CPython qu'une fois que vous savez que ce que vous faites est sensé et faisable.

18
abarnert

Après avoir lu la réponse de thg4535, je suis sûr que vous trouverez intéressantes les explications suivantes sur ceval.c: Bonjour, ceval.c!

Cet article fait partie d'une série écrite par Yaniv Aknin dont je suis une sorte de fan: Python's Innards

5
eyquem