Låt oss få lite ”meta” om programmering.
hur vet Python-programmet (bättre som tolk)” hur man kör din kod?Om du är ny på programmering kan det verka som magic.In faktum, det verkar fortfarande som magi för mefter att ha varit professionelli mer än ett decennium.
Python-tolken är inte magisk(ledsen att göra dig besviken).det följer en förutsägbar uppsättning steg för att översätta din kod till instruktioner som en maskin kan köra.
på en ganska hög nivå, här är vad som händer med din kod:
- koden analyseras (dvs delas upp) i en lista med bitar som vanligtvis kallas tokens.Dessa tokens är baserade på en uppsättning reglerför saker som bör behandlas annorlunda.Till exempel är nyckelordet
if
en annan token än ett numeriskt värde som42
. - den råa listan över tokens transformerasför att bygga ett abstrakt syntaxträd, AST, vilket är ämnet vi kommer att utforska mer i det här inlägget.En AST är en samling nodarsom är länkade tillsammansbaserat på grammatiken i Python-språket.Oroa dig inte om det inte var meningsfullt nueftersom vi kommer att lysa mer ljus på det tillfälligt.
- från ett abstrakt syntaxträd kan tolken producera en lägre nivåform av instruktionerkallad bytekod.Dessa instruktioner är saker som
BINARY_ADD
och är avsedda att vara mycket generiskaså att en dator kan köra dem. - med bytecode-instruktionerna tillgängliga kan tolken äntligen köra din kod.Bytekoden används för att ringa funktioneri ditt operativsystemvilket i slutändan kommer att interagera med en CPU och minneatt köra programmet.
många fler detaljer kan passa in i den beskrivningen,men det är den grova skissen av hur typade tecken exekveras av datorprocessorer.
ASTs som analysverktyg
När källkoden omvandlas till bytekod är det för sent att få mycket förståelse för vad du skrev.Bytecode är mycket primitivoch mycket inställd på att göra tolken fast.In andra ord,bytecode är utformad för datorer över människor.
å andra sidan har abstrakta syntaxträd tillräckligt strukturerad informationinom dem för att göra dem användbara för att lära sig om din kod.ASTs är fortfarande inte mycket vänliga människor, men de är mer förnuftiga än bytecode-representationen.
eftersom Python är ett” batteries included ” – språk är verktygen du behöver använda AST inbyggda i standardbiblioteket.
det primära verktyget för att arbeta med AST är modulenast
.Låt oss titta på ett exempel för att se hur detta fungerar.
ast med exempel
nedan är exemplet Python-skript som vi ska använda.Detta skript svarar på frågan om ” vilka moduler importerades?”
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()
den här koden gör ett par viktiga saker:
- omvandlar en Python-fils text(i det här fallet själva exempelkoden)till ett abstrakt syntaxträd.
- analyserar AST för att extrahera viss information ur den.
Du kan köra den här koden som:
$ python3 ast_example.py{'from': , 'import': }
omvandla till AST
with open("ast_example.py", "r") as source: tree = ast.parse(source.read())
i två rader kod läser vi en fil och skapar en ast med namnet tree
.Funktionen ast.parse
gör det enkelt!Det händer massor under huven på den funktionen som vi lyckligt kan ignorera.
med ett funktionssamtal behandlade Python alla tokens, följde alla språkregler och byggde en datastruktur (dvs., ett träd) som innehåller all relevant informationatt köra koden.
innan vi går vidare,Låt oss ta en stund att överväga vad ett träd är.Träd är en mycket djup topicin mjukvaruutvecklingså betrakta detta som en primärsnarare än en uttömmande förklaring.
ett träd är ett sätt att hålla datasom en uppsättning ”noder” anslutna med ” kanter.”
+-----+ | A | +-----+ / \ / \+-----+ +-----+| B | | C |+-----+ +-----+
i detta diagram är A,B och C alla noderoch det finns kanter som ansluter A till B och A till C.
ett sätt att representera detta träd i kod kan vara:
class Node: def __init__(self, value): self.value = value self.children = tree = Node('A')tree.children.append(Node('B'))tree.children.append(Node('C'))
Observera att tree
är faktiskt en nod!När vi arbetar med ett träd har vi verkligen att göra med en samling noder,och trädvariabeln är en hänvisning till ”root” – noden(t.ex.nod A). genom att ha denna typ av struktur kan vi kontrollera varje nod i trädetoch vidta åtgärder.Vi gör det genom att besöka varje nod i trädetoch bearbeta dess data.
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
Nu när vi har en uppfattning om vad ett träd är,kan vi överväga vad nästa avsnittav exempelskriptet gör.Trädstrukturen i Pythons abstrakta syntaxträd är mer involveradpå grund av räkningen av dess noderoch typen av data som lagras,men kärntanken om noder och kanter är densamma.
analysera AST
När vi har trädet följer Analyzer
det besöksmönster som jag visade ovanföratt extrahera information ur trädet.
Jag noterade att en Python AST är mer komplexän min grundläggande Node
design.En skillnad är att den spårar olika typer av noder.Det är här ast.NodeVisitor
är användbart.
A NodeVisitor
kan svara på alla typer av nodei Python AST.To besök en viss typ av nod,vi måste implementera en metodsom ser ut som visit_<node type>
.
min exempelkod försöker ta reda på om imports.To läs om import, koden drar frånImport
ochImportFrom
nodtyper.
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)
denna kod har namnet på modulenoch lagrar deni en lista med statistik.Medan koden inte är snygg,visar den hur du interagerar med AST-noder.
medNodeVisitor
klass definierad kan vi använda den för att analysera trädet.
analyzer = Analyzer()analyzer.visit(tree)
visit
metoden kommer att delegera till din visit_<node type>
metodnär den typen av nod är encounteredmedan du korsar genom trädstrukturen.
Så, vilka typer av nodtyper finns det?Du hittar hela listan iabstrakt grammatik sektionavast
moduldokumentation.Sanningsenligt, jag tycker att dokumentationen lite svårt att absorbera.Du kan ha mer framgånggenom att hänvisa till en mer uttömmande guide somgreen Tree Snakes Nodes guide.
förpackning upp
nu förstår du förhoppningsvis hur man:
- bygga en AST från Python källkod.
- gör analys på AST med ett
NodeVisitor
.
Jag tror att du kan svara på många intressanta frågorom din kodgenom att använda abstrakta syntaxträd.Frågor som:
- hur många variabler använde jag?
- vilka är de vanligaste funktionssamtalen i min kod?
- är mina moduler tätt kopplade till varandra?
- vilka tredjepartsbibliotek visas ofta i olika paket?
ast
modulen är förmodligen inte ett verktygsom du kommer att nå för mycket often.In de gånger som du behöver ast
,dess minimala API är ganska minnesvärdoch du kan analysera kod snabbt.
Om du tyckte att det här var användbart, skulle du ha något emot att dela detta på Twittereller din favorit sociala medier webbplats?Jag gillar att chatta med människorom dessa typer av ämnenså gärna tweet mig på@mblayman.
lär dig mer om Python!
Du kan gå med i mitt nyhetsbrev tillsammans med 1000+ andra utvecklare för att hjälpa dig att lära dig mer om Django och Python.