Lassen Sie uns ein wenig „Meta“ über die Programmierung bekommen.
Wie“weiß“das Python-Programm(besser bekannt als Interpreter), wie Sie Ihren Code ausführen?Wenn Sie neu in der Programmierung sind, mag es so aussehen magic.In tatsache, es scheint mir immer noch wie Magie, nachdem ich seit mehr als einem Jahrzehnt Profi bin.
Der Python-Interpreter ist keine Magie (tut mir leid, Sie zu enttäuschen).Er folgt einem vorhersehbaren Satz von Schritten, um Ihren Code in Anweisungen zu übersetzen, die eine Maschine ausführen kann.
Auf einer ziemlich hohen Ebene passiert Folgendes mit Ihrem Code:
- Der Code wird in eine Liste von Teilen analysiert (dh aufgeteilt), die normalerweise als Token bezeichnet werden.Diese Token basieren auf einer Reihe von Regelnfür Dinge, die anders behandelt werden sollten.Zum Beispiel ist das Schlüsselwort
if
ein anderes Token als ein numerischer Wert wie42
. - Die rohe Liste der Token wird transformiertum einen abstrakten Syntaxbaum, AST, zu erstellen, der das Thema ist, das wir in diesem Beitrag näher untersuchen werden.Ein AST ist eine Sammlung von Knoten, die miteinander verknüpft sindbasierend auf der Grammatik der Python-Sprache.Mach dir keine Sorgen, wenn das jetzt keinen Sinn ergibt, denn wir werden für einen Moment mehr Licht darauf werfen.
- Aus einem abstrakten Syntaxbaum kann der Interpreter eine untergeordnete Form von Anweisungen erzeugen, die Bytecode genannt wird.Diese Anweisungen sind Dinge wie
BINARY_ADD
und sollen sehr allgemein seinso dass ein Computer sie ausführen kann. - Mit den verfügbaren Bytecode-Anweisungen kann der Interpreter schließlich Ihren Code ausführen.Der Bytecode wird verwendet, um Funktionen in Ihrem Betriebssystem aufzurufen, die letztendlich mit einer CPU und einem Speicher interagieren, um das Programm auszuführen.
Viele weitere Details könnten in diese Beschreibung passen, aber das ist die grobe Skizze, wie typisierte Zeichen von Computer-CPUs ausgeführt werden.
Sowie Analysetools
Wenn Ihr Quellcode in Bytecode umgewandelt wird, ist es zu spät, um viel Verständnis für das zu gewinnen, was Sie geschrieben haben.Bytecode ist sehr primitiv und sehr darauf eingestellt, den Interpreter zu erstellen fast.In mit anderen Worten,Bytecode ist für Computer über Menschen konzipiert.
Auf der anderen Seite haben abstrakte Syntaxbäume genügend strukturierte Informationen, um sie nützlich zu machen, um etwas über Ihren Code zu lernen.ASTs sind immer noch nicht sehr menschenfreundlich, aber sie sind vernünftiger als die Bytecode-Darstellung.
Da Python eine „batteries included“ -Sprache ist, sind die Werkzeuge, die Sie für die Verwendung von ASTs benötigen, in die Standardbibliothek integriert.
Das primäre Werkzeug, um mit ASTs zu arbeiten, ist das ast
Modul.Schauen wir uns ein Beispiel an, um zu sehen, wie das funktioniert.
ast by example
Unten ist das Beispiel-Python-Skript, das wir verwenden werden.Dieses Skript beantwortet die Frage „Welche Module wurden importiert?“
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()
Dieser Code macht ein paar wichtige Dinge:
- Transformiert den Text einer Python-Datei(in diesem Fall den Beispielcode selbst)in einen abstrakten Syntaxbaum.
- Analysiert den AST, um einige Informationen daraus zu extrahieren.
Sie können diesen Code als ausführen:
$ python3 ast_example.py{'from': , 'import': }
In AST umwandeln
with open("ast_example.py", "r") as source: tree = ast.parse(source.read())
In zwei Codezeilen lesen wir eine Datei und erstellen einen AST mit dem Namen tree
.Die ast.parse
Funktion macht dies zum Kinderspiel!Unter der Haube dieser Funktion passiert eine Menge, die wir glückselig ignorieren können.
Mit einem Funktionsaufruf verarbeitete Python alle Token, befolgte alle Regeln der Sprache und baute eine Datenstruktur auf (z., ein Baum), der alle relevanten Informationen zum Ausführen des Codes enthält.
Bevor wir fortfahren,nehmen wir uns einen Moment Zeit, um zu überlegen, was ein Baum ist.Bäume sind ein sehr tiefes Thema in der Softwareentwicklungbetrachten Sie dies daher eher als eine erschöpfende Erklärung.
Ein Baum ist eine Möglichkeit, Daten in einer Reihe von „Knoten“ zu halten, die durch „Kanten“ verbunden sind.“
+-----+ | A | +-----+ / \ / \+-----+ +-----+| B | | C |+-----+ +-----+
In diesem Diagramm sind A, B und C alle Knotenund es gibt Kanten, die A mit B und A mit C verbinden.
Eine Möglichkeit, diesen Baum im Code darzustellen, könnte sein:
class Node: def __init__(self, value): self.value = value self.children = tree = Node('A')tree.children.append(Node('B'))tree.children.append(Node('C'))
Beachten Sie, dass die tree
ist eigentlich ein Knoten!Wenn wir mit einem Baum arbeiten, haben wir es wirklich mit einer Sammlung von Knoten zu tun, und die Baumvariable ist ein Verweis auf den „Stamm“ -Knoten (z. B. Knoten A).Durch diese Art von Struktur können wir jeden Knoten im Baum überprüfenund Maßnahmen ergreifen.Wir tun dies, indem wir jeden Knoten im Baum besuchenund seine Daten verarbeiten.
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
Nun, da wir eine Vorstellung davon haben, was ein Baum ist, können wir überlegen, was der nächste Abschnittdes Beispielskripts tut.Die Baumstrukturdes abstrakten Python-Syntaxbaums ist aufgrund der Anzahl seiner Knoten und der Art der gespeicherten Daten stärker involviert, doch die Kernidee von Knoten und Kanten ist dieselbe.
Analysiere den AST
Sobald wir den Baum haben, folgt der Analyzer
dem Besuchermuster, das ich oben gezeigt habe, um Informationen aus dem Baum zu extrahieren.
Ich habe festgestellt, dass ein Python-AST komplexer ist als mein grundlegendes Node
Design.Ein Unterschied besteht darin, dass verschiedene Arten von Knoten verfolgt werden.Hier ist ast.NodeVisitor
nützlich.
A NodeVisitor
kann auf jede Art von Knoten reagierenim Python AST.To für einen bestimmten Knotentyp müssen wir eine Methode implementierendas sieht aus wie visit_<node type>
.
Mein Beispielcode versucht es herauszufinden. imports.To erfahren Sie mehr über Importe,der Code zieht aus den Knotentypen Import
und 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)
Dieser Code nimmt den Namen des Moduls an und speichert ihn in einer Statistikliste.Obwohl der Code nicht ausgefallen ist, zeigt er Ihnen, wie Sie mit AST-Knoten interagieren.
Wenn die NodeVisitor
Klasse definiert ist, können wir sie verwenden, um den Baum zu analysieren.
analyzer = Analyzer()analyzer.visit(tree)
Die visit
-Methode wird an Ihre visit_<node type>
-Methode delegiert, wenn dieser Knotentyp beim Durchlaufen der Baumstruktur auftritt.
Also, welche Arten von Knotentypen gibt es?Die vollständige Liste finden Sie im Abschnitt „Abstrakte Grammatik“ der ast
Moduldokumentation.Ehrlich gesagt finde ich diese Dokumentation etwas schwer zu absorbieren.Möglicherweise haben Sie mehr Erfolg, wenn Sie sich auf einen ausführlicheren Leitfaden wie den Green Tree Snakes Nodes Guide beziehen.
Einpacken
Inzwischen verstehen Sie hoffentlich, wie man:
- Einen AST aus Python-Quellcode erstellt.
- Analysieren Sie den AST mit einem
NodeVisitor
.
Ich denke, Sie können viele interessante Fragen zu Ihrem Code beantwortenmit abstrakten Syntaxbäumen.Fragen wie:
- Wie viele Variablen habe ich verwendet?
- Was sind die häufigsten Funktionsaufrufe in meinem Code?
- Sind meine Module eng miteinander gekoppelt?
- Welche Bibliotheken von Drittanbietern werden häufig in verschiedenen Paketen angezeigt?
Das ast
-Modul ist wahrscheinlich kein Werkzeug, nach dem Sie sehr schnell greifen werden often.In diese Zeiten, die Sie brauchen ast
, seine minimale API ist ziemlich einprägsamund Sie können Code schnell analysieren.
Wenn Sie dies nützlich fanden, würde es Ihnen etwas ausmachen, dies auf Twitter oder Ihrer bevorzugten Social-Media-Site zu teilen?Ich mag es, mit Leuten über diese Art von Themen zu plaudern, also zögern Sie nicht, mich unter @mblayman zu twittern.
Erfahren Sie mehr über Python!
Sie können meinen Newsletter zusammen mit mehr als 1.000 anderen Entwicklern abonnieren, um mehr über Django und Python zu erfahren.