Să luăm un pic „meta” despre programare.
Cum programul Python(mai bine știu ca interpret)”știu” cum să ruleze codul?Dacă sunteți nou în programare,poate părea magic.In de fapt, mi se pare încă magiedupă ce am fost profesionistpentru mai mult de un deceniu.
interpretul Python nu este magic(îmi pare rău să vă dezamăgesc).urmează un set previzibil de pași pentru a traduce codul dvs. în instrucțiunile pe care le poate rula o mașină.
la un nivel destul de ridicat,iată ce se întâmplă cu codul dvs.:
- codul este analizat (adică împărțit) într-o listă de piese numite de obicei jetoane.Aceste jetoane se bazează pe un set de regulipentru lucruri care ar trebui tratate diferit.De exemplu,cuvântul cheie
if
este un simbol diferit de o valoare numerică precum42
. - lista brută de jetoane este transformatăpentru a construi un arbore de sintaxă Abstract, AST,care este subiectul pe care îl vom explora mai mult în acest post.Un AST este o colecție de noduricare sunt legate împreunăbazat pe gramatica limbajului Python.Nu vă faceți griji dacă asta nu are nici un sens acum, deoarece vom străluci mai multă lumină asupra ei momentan.
- dintr-un arbore de sintaxă abstractă,interpretul poate produce un nivel inferior formof instrucțiuni numit bytecode.Aceste instrucțiuni sunt lucruri precum
BINARY_ADD
și sunt menite să fie foarte genericastfel încât un computer să le poată rula. - cu instrucțiunile bytecode disponibile, interpretul poate rula în cele din urmă codul.Bytecode este folosit pentru a apela funcțiiîn sistemul dvs. de operarecare va interacționa în cele din urmă cu un procesor și memoriepentru a rula programul.
multe alte detalii s-ar putea încadra în acea descriere,dar aceasta este schița brută a modului în care caracterele tastatesunt executate de procesoarele computerului.
ASTs ca instrumente de analiză
până când codul sursă este transformat în bytecode,este prea târziu pentru a obține mult understandingabout ceea ce ai scris.Bytecode este foarte primitivși foarte reglat pentru a face interpretul fast.In cu alte cuvinte,bytecode este proiectat pentru calculatoare peste oameni.
pe de altă parte,arborii de sintaxă abstractă au suficiente informații structurate în cadrul lorpentru a le face utilepentru a învăța despre codul dvs.ASTs încă nu sunt foarte prietenoși,dar sunt mai sensibili decât reprezentarea bytecode.
deoarece Python este un limbaj „baterii incluse”, instrumentele de care aveți nevoie pentru a utiliza ASTs sunt încorporate în biblioteca standard.
instrumentul principal pentru a lucra cu ASTs este modululast
.Să ne uităm la un exemplu pentru a vedea cum funcționează acest lucru.
ast de exemplu
mai jos este exemplul script Python pe care îl vom folosi.Acest script răspunde la întrebarea ” Ce module au fost importate?”
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()
acest cod face câteva lucruri majore:
- transformă textul unui fișier Python(în acest caz, Codul De exemplu în sine)într-un arbore de sintaxă abstractă.
- analizează AST pentru a extrage unele informații din ea.
puteți rula acest cod ca:
$ python3 ast_example.py{'from': , 'import': }
transforma la AST
with open("ast_example.py", "r") as source: tree = ast.parse(source.read())
în două linii de cod,citim un fișier și creăm un ast numittree
.Funcția ast.parse
face acest lucru într-o clipă!Există o tonă care se întâmplă sub capota acestei funcții pe care o putem ignora fericit.
cu un apel de funcție,Python a procesat toate jetoanele,a respectat toate regulile limbii și a construit o structură de date (adică., un copac) care conține toate informațiile relevantepentru a rula codul.
înainte de a merge mai departe,să luăm un moment pentru a lua în considerare ce este un copac.Copacii sunt un topic foarte profundîn dezvoltarea software-uluiastfel încât să ia în considerare acest lucru un primermai degrabă decât o explicație exhaustivă.
un arbore este o modalitate de a reține dateleca un set de „noduri” conectate prin „muchii”.”
+-----+ | A | +-----+ / \ / \+-----+ +-----+| B | | C |+-----+ +-----+
În această diagramă,a, B și C sunt toate noduriși există muchii care leagă A de B și a de C.
o modalitate de a reprezenta acest arbore în cod ar putea fi:
class Node: def __init__(self, value): self.value = value self.children = tree = Node('A')tree.children.append(Node('B'))tree.children.append(Node('C'))
observați cătree
este de fapt un nod!Când lucrăm cu un copac,avem de-a face cu o colecție de noduri,iar variabila arbore este o referință la nodul „rădăcină” (de exemplu, nodul A).având acest tip de structură,putem verifica fiecare nod din copacși să luăm măsuri.Facem acest lucru vizitând fiecare nodîn copacși prelucrarea datelor sale.
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
acum că avem o idee despre ce este un copac,putem lua în considerare ce face următoarea secțiunedin scriptul de exemplu.Structura arborelui arborelui sintaxei abstracte Python este mai implicatădin cauza numărului de noduri și a tipului de date stocate,totuși ideea de bază a nodurilor și marginilor este aceeași.
analizați AST
odată ce avem arborele,Analyzer
urmează modelul de vizitator pe care l-am arătat mai suspentru a extrage informații din copac.
am observat că un AST Python este mai complex decât designul meu de bazăNode
.O diferență este că urmărește diferite tipuri de noduri.Aici este utilast.NodeVisitor
.
aNodeVisitor
poate răspunde la orice tip de nodîn Python AST.To vizitați un anumit tip de nod,trebuie să implementăm o metodăcare arată ca visit_<node type>
.
codul meu de exemplu încearcă să afle despre imports.To aflați mai multe despre importuri, codul trage de laImport
șiImportFrom
tipuri de noduri.
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)
acest cod ia numele modulului și îl stochează într-o listă de statistici.În timp ce codul nu este fantezist,vă arată cum să interacționațicu nodurile AST.
cuNodeVisitor
clasa definită,o putem folosi pentru a analiza arborele.
analyzer = Analyzer()analyzer.visit(tree)
visit
metoda va delega la visit_<node type>
metoda când acel tip de nod este întâlnitîn timp ce traversează structura arborelui.
Deci, ce tipuri de tipuri de noduri există?Puteți găsi lista completă însecțiunea de gramatică abstractădin documentația modululuiast
.Sincer, mi se pare că documentația un pic cam greu de absorbit.Este posibil să aveți mai mult succes, referindu-vă la un ghid mai exhaustiv, cum ar fi Ghidul nodurilor șerpilor verzi.
împachetând
până acum,sperăm că înțelegeți cum să:
- construiți un AST din Codul sursă Python.
- face analiza pe AST folosind un
NodeVisitor
.
cred că puteți răspunde la multe întrebări interesantedespre codul dvs. prin utilizarea arborilor de sintaxă abstractă.Întrebări precum:
- câte variabile am folosit?
- care sunt cele mai frecvente apeluri de funcții din Codul meu?
- modulele mele sunt strâns legate între ele?
- ce biblioteci terțe apar frecventîn diferite pachete?
ast
modulul nu este probabil un instrumentcare va ajunge pentru foarte often.In acele momente în care aveți nevoie ast
,API-ul său minim este destul de memorabilși puteți analiza codul rapid.
dacă ați găsit acest lucru util,v-ar deranja să împărtășiți acest lucru pe Twittersau pe site-ul dvs. preferat de socializare?Îmi place să discut cu oamenidespre astfel de subiecte, așa că nu ezitați să mă trimiteți pe Twitter la@mblayman.
Aflați mai multe despre Python!
vă puteți alătura newsletter-ul meu, împreună cu 1.000+ alți dezvoltatori pentru a vă ajuta să aflați mai multe despre Django și Python.