Prenons un peu de « méta” sur la programmation.
Comment le programme Python (mieux connu sous le nom d’interpréteur) « sait” comment exécuter votre code?Si vous êtes nouveau dans la programmation, cela peut sembler magic.In fait, cela me semble toujours magique après avoir été un professionnel pendant plus d’une décennie.
L’interpréteur Python n’est pas magique (désolé de vous décevoir). Il suit un ensemble prévisible d’étapes pour traduire votre code en instructions qu’une machine peut exécuter.
À un niveau assez élevé, voici ce qui arrive à votre code:
- Le code est analysé (c’est-à-dire divisé) en une liste de pièces généralement appelées jetons.Ces jetons sont basés sur un ensemble de règlespour les choses qui devraient être traitées différemment.Par exemple, le mot clé
if
est un jeton différent d’une valeur numérique comme42
. - La liste brute de jetons est transforméepour construire un arbre de syntaxe abstraite, AST, qui est le sujet que nous explorerons plus dans cet article.Un AST est une collection de nœuds qui sont liés ensemble sur la base de la grammaire du langage Python.Ne vous inquiétez pas si cela n’a aucun sens maintenant, car nous allons y faire plus de lumière momentanément.
- À partir d’un arbre syntaxique abstrait, l’interpréteur peut produire un formulaire d’instructions de niveau inférieur appelé bytecode.Ces instructions sont des choses comme
BINARY_ADD
et sont censées être très génériques pour qu’un ordinateur puisse les exécuter. - Avec les instructions de bytecode disponibles, l’interpréteur peut enfin exécuter votre code.Le bytecode est utilisé pour appeler des fonctions dans votre système d’exploitation qui interagiront finalement avec un processeur et une mémoire pour exécuter le programme.
Beaucoup plus de détails pourraient entrer dans cette description, mais c’est l’esquisse approximative de la façon dont les caractères typés sont exécutés par les processeurs informatiques.
Les AST en tant qu’outils d’analyse
Au moment où votre code source est transformé en bytecode, il est trop tard pour mieux comprendre ce que vous avez écrit.Le bytecode est très primitif et très à l’écoute pour rendre l’interpréteur fast.In en d’autres termes, le bytecode est conçu pour les ordinateurs sur les personnes.
D’un autre côté, les arbres syntaxiques abstraits contiennent suffisamment d’informations structurées pour les rendre utiles à l’apprentissage de votre code.Les AST ne sont toujours pas très conviviaux pour les gens, mais ils sont plus sensibles que la représentation de bytecode.
Comme Python est un langage ”piles incluses », les outils dont vous avez besoin pour utiliser les AST sont intégrés à la bibliothèque standard.
L’outil principal pour travailler avec les AST est le module ast
.Regardons un exemple pour voir comment cela fonctionne.
ast par exemple
Voici l’exemple de script Python que nous allons utiliser.Ce script répond à la question » quels modules ont été importés ?”
import astfrom pprint import pprintdef main():with open("ast_example.py", "r") as source:tree = ast.parse(source.read())
analyzer <span style="color:#f92672">=</span> Analyzer()analyzer<span style="color:#f92672">.</span>visit(tree)analyzer<span style="color:#f92672">.</span>report()
class Analyzer(ast.NodeVisitor):def init(self):self.stats = {« import »: , « from »: }
<span style="color:#66d9ef">def</span> <span style="color:#a6e22e">visit_Import</span>(self, node): <span style="color:#66d9ef">for</span> alias <span style="color:#f92672">in</span> node<span style="color:#f92672">.</span>names: self<span style="color:#f92672">.</span>stats<span style="color:#f92672">.</span>append(alias<span style="color:#f92672">.</span>name) self<span style="color:#f92672">.</span>generic_visit(node)<span style="color:#66d9ef">def</span> <span style="color:#a6e22e">visit_ImportFrom</span>(self, node): <span style="color:#66d9ef">for</span> alias <span style="color:#f92672">in</span> node<span style="color:#f92672">.</span>names: self<span style="color:#f92672">.</span>stats<span style="color:#f92672">.</span>append(alias<span style="color:#f92672">.</span>name) self<span style="color:#f92672">.</span>generic_visit(node)<span style="color:#66d9ef">def</span> <span style="color:#a6e22e">report</span>(self): pprint(self<span style="color:#f92672">.</span>stats)
if name == « main »:main()
Ce code fait quelques choses importantes:
- Transforme le texte d’un fichier Python (dans ce cas, l’exemple de code lui-même) en un arbre syntaxique abstrait.
- Analyse l’AST pour en extraire certaines informations.
Vous pouvez exécuter ce code comme:
$ python3 ast_example.py{'from': , 'import': }
Transformer en AST
with open("ast_example.py", "r") as source: tree = ast.parse(source.read())
En deux lignes de code, nous lisons un fichier et créons un AST nommé tree
.La fonction ast.parse
en fait un jeu d’enfant!Il y a une tonne qui se passe sous le capot de cette fonctionque nous pouvons ignorer béatement.
Avec un appel de fonction, Python a traité tous les jetons, a suivi toutes les règles du langage et a construit une structure de données (i.e., une arborescence) contenant toutes les informations pertinentes pour exécuter le code.
Avant de passer à autre chose, prenons un moment pour réfléchir à ce qu’est un arbre.Les arbres sont un sujet très profonddans le développement de logiciels, considérez-le comme une explication plutôt qu’une explication exhaustive.
Un arbre est un moyen de contenir des données comme un ensemble de « nœuds” reliés par des « bords ».”
+-----+ | A | +-----+ / \ / \+-----+ +-----+| B | | C |+-----+ +-----+
Dans ce diagramme, A, B et C sont tous des nœuds et il y a des bords reliant A à B et A à c.
Une façon de représenter cet arbre dans le code pourrait être:
class Node: def __init__(self, value): self.value = value self.children = tree = Node('A')tree.children.append(Node('B'))tree.children.append(Node('C'))
Notez que le tree
est en fait un nœud!Lorsque nous travaillons avec un arbre, nous avons vraiment affaire à une collection de nœuds, et la variable d’arbre est une référence au nœud « racine” (par exemple, le nœud A). En ayant ce type de structure, nous pouvons vérifier chaque nœud dans l’arboretumet agir.Nous le faisons en visitant chaque nœud dans l’arboreet en traitant ses données.
def print_node_value(value): print(value)def visit(node, handle_node): handle_node(node.value) for child in node.children: visit(child, handle_node)# tree is from the previous example.visit(tree, print_node_value)# This should print:# A# B# C
Maintenant que nous avons une idée de ce qu’est un arbre, nous pouvons considérer ce que fait la section suivante de l’exemple de script.La structure arborescente de l’arbre de syntaxe abstraite Python est plus impliquée en raison du nombre de ses nœuds et du type de données stockées, mais l’idée de base des nœuds et des arêtes est la même.
Analysez l’AST
Une fois que nous avons l’arbre, le Analyzer
suit le modèle de visiteur que j’ai montré ci-dessus pour extraire des informations de l’arbre.
J’ai noté qu’un AST Python est plus complexe que ma conception de base Node
.Une différence est qu’il suit différents types de nœuds.C’est là que ast.NodeVisitor
est utile.
Un NodeVisitor
peut répondre à tout type de nœud dans le Python AST.To visitez un type particulier de nœud, nous devons implémenter une méthodequi ressemble à visit_<node type>
.
Mon exemple de code essaie de savoir à propos de imports.To en savoir plus sur les importations, le code tire des types de nœuds Import
et ImportFrom
.
def visit_Import(self, node): for alias in node.names: self.stats.append(alias.name) self.generic_visit(node)def visit_ImportFrom(self, node): for alias in node.names: self.stats.append(alias.name) self.generic_visit(node)
Ce code prend le nom du module et le stocke dans une liste de statistiques.Bien que le code ne soit pas sophistiqué, il vous montre comment interagir avec les nœuds AST.
Avec la classe NodeVisitor
définie, nous pouvons l’utiliser pour analyser l’arborescence.
analyzer = Analyzer()analyzer.visit(tree)
La méthode visit
déléguera à votre méthode visit_<node type>
quel que soit le type de nœud utilisé rencontréen traversant la structure arborescente.
Alors, quels types de types de nœuds existe-t-il ?Vous pouvez trouver la liste complète dans la section Grammaire abstraite de la documentation du module ast
.À vrai dire, je trouve cette documentation un peu difficile à absorber.Vous aurez peut-être plus de succèsen vous référant à un guide plus exhaustif comme le guide des nœuds de serpents d’arbres verts.
En terminant
Maintenant, vous comprenez, espérons-le, comment:
- Construire un AST à partir du code source Python.
- Effectuez une analyse sur l’AST en utilisant un
NodeVisitor
.
Je pense que vous pouvez répondre à de nombreuses questions intéressantes sur votre code en utilisant des arbres de syntaxe abstraits.Des questions comme:
- Combien de variables ai-je utilisé?
- Quels sont les appels de fonction les plus courants dans mon code ?
- Mes modules sont-ils étroitement couplés les uns aux autres ?
- Quelles bibliothèques tierces apparaissent fréquemment dans différents paquets ?
Le module ast
n’est probablement pas un outil que vous atteindrez pour très often.In les fois où vous avez besoin de ast
, son API minimale est assez mémorisable et vous pouvez analyser le code rapidement.
Si cela vous a été utile, cela vous dérangerait-il de le partager sur Twitter ou sur votre site de médias sociaux préféré?J’aime discuter avec des gens sur ce genre de sujets, n’hésitez pas à me tweeter à @mblayman.
En savoir plus sur Python !
Vous pouvez rejoindre ma newsletter avec plus de 1 000 autres développeurs pour vous aider à en savoir plus sur Django et Python.