przejdźmy trochę „meta” do programowania.
w jaki sposób program Python(lepiej znany jako interpreter)”wie”, jak uruchomić kod?Jeśli jesteś nowy w programowaniu, może się wydawać, że magic.In fakt, to nadal wydaje się magicznepo bycie profesjonalistą od ponad dekady.
interpreter Pythona nie jest magiczny(przykro mi, że cię rozczaruję).postępuje zgodnie z przewidywalnym zestawem kroków do przetłumaczenia kodu na instrukcje, które maszyna może uruchomić.
na dość wysokim poziomie,oto co dzieje się z Twoim kodem:
- kod jest parsowany (tj. podzielony) na listę elementów Zwykle nazywanych tokenami.Tokeny te są oparte na zestawie regułdla rzeczy, które powinny być traktowane inaczej.Na przykład słowo kluczowe
if
jest innym tokenem niż wartość liczbowa,taka jak42
. - surowa lista tokenów została przekształcona w celu zbudowania abstrakcyjnego drzewa składniowego, AST,który jest tematem, który omówimy więcej w tym poście.AST jest zbiorem nodów, które są połączone ze sobą na podstawie gramatyki języka Python.Nie martw się, jeśli to nie ma sensu teraz, ponieważ za chwilę rzucimy na to więcej światła.
- z abstrakcyjnego drzewa składni,interpreter może wytworzyć niższy poziom formof instructionscalled bytecode.Instrukcje te są takie jak
BINARY_ADD
I mają być bardzo ogólne, aby komputer mógł je uruchomić. - z dostępnymi instrukcjami kodu bajtowego, interpreter może w końcu uruchomić twój kod.Kod bajtowy służy do wywoływania funkcji w systemie operacyjnym, które ostatecznie będą współdziałać z procesorem i pamięcią, aby uruchomić program.
wiele więcej szczegółów może zmieścić się w tym opisie,ale to jest przybliżony szkic tego, jak wpisane znaki są wykonywane przez procesory komputerowe.
ASTs jako narzędzia analityczne
zanim twój kod źródłowy zostanie zamieniony w bajt,jest już za późno na zrozumienie tego, co napisałeś.Bytecode jest bardzo prymitywny i bardzo dostrojony do tworzenia interpretera fast.In innymi słowy, bytecode jest przeznaczony dla komputerów nad ludźmi.
z drugiej strony,abstrakcyjne drzewa składniowe mają wystarczająco dużo uporządkowanych informacji, aby były przydatne do nauki o kodzie.ASTs nadal nie są zbyt przyjazne dla ludzi, ale są bardziej rozsądne niż reprezentacja kodu bajtowego.
ponieważ Python jest językiem „dołączonym do baterii”, narzędzia potrzebne do korzystania z ASTs są wbudowane w standardową bibliotekę.
podstawowym narzędziem do pracy z ASTs jest modułast
.Spójrzmy na przykład, aby zobaczyć, jak to działa.
ast na przykładzie
Poniżej znajduje się przykładowy skrypt Pythona, którego użyjemy.Ten skrypt odpowiada na pytanie ” jakie moduły zostały zaimportowane?”
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()
ten kod robi kilka głównych rzeczy:
- przekształca tekst pliku Pythona(w tym przypadku sam przykładowy kod)w abstrakcyjne drzewo składniowe.
- analizuje AST, aby wydobyć z niego pewne informacje.
możesz uruchomić ten kod jako:
$ python3 ast_example.py{'from': , 'import': }
Przekształć na AST
with open("ast_example.py", "r") as source: tree = ast.parse(source.read())
w dwóch wierszach kodu odczytujemy plik i tworzymy AST o nazwie tree
.Funkcja ast.parse
sprawia, że jest to bardzo proste!Pod maską tej funkcji dzieje się mnóstwo rzeczy, które możemy błogie zignorować.
za pomocą jednego wywołania funkcji,Python przetwarzał wszystkie tokeny,przestrzegał wszystkich reguł języka i zbudował strukturę danych (tj., drzewo) zawierające wszystkie istotne informacje do uruchomienia kodu.
zanim przejdziemy dalej,zastanówmy się, czym jest drzewo.Drzewa są bardzo głęboką tematykąw rozwoju oprogramowania, uważając to za primerrather niż wyczerpujące wyjaśnienie.
drzewo jest sposobem przechowywania danych jako zbiór „węzłów” połączonych „krawędziami”.”
+-----+ | A | +-----+ / \ / \+-----+ +-----+| B | | C |+-----+ +-----+
na tym diagramie A,B I C są węzłami i istnieją krawędzie łączące A Z B I A Z C.
jednym ze sposobów reprezentowania tego drzewa w kodzie może być:
class Node: def __init__(self, value): self.value = value self.children = tree = Node('A')tree.children.append(Node('B'))tree.children.append(Node('C'))
zauważ, żetree
jest w rzeczywistości węzłem!Kiedy pracujemy z drzewem, tak naprawdę mamy do czynienia ze zbiorem węzłów, a zmienna drzewa jest odniesieniem do węzła „root” (np. węzeł a).mając taką strukturę,możemy sprawdzić każdy węzeł w drzewie i podjąć działania.Robimy to odwiedzając każdy punkt w drzewie i przetwarzając jego dane.
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
teraz,gdy mamy pojęcie o tym, czym jest drzewo, możemy rozważyć, co robi następna sekcja przykładowego skryptu.Struktura drzewa abstrakcyjnego drzewa składniowego Pythona jest bardziej zaangażowana ze względu na liczbę jego węzłów i typ przechowywanych danych, jednak podstawowa idea węzłów i krawędzi jest taka sama.
Analizuj AST
gdy mamy drzewo,Analyzer
podąża za wzorcem odwiedzającego, który pokazałem powyżej, aby wyodrębnić informacje z drzewa.
zauważyłem, że Python AST jest bardziej złożony niż mój podstawowy projektNode
.Jedną z różnic jest to, że śledzi różne typy węzłów.Tutaj przydaje się ast.NodeVisitor
.
a NodeVisitor
może reagować na dowolny typ nod w Pythonie AST.To odwiedzając dany typ węzła, musimy zaimplementować metodę, która wygląda jak visit_<node type>
.
mój przykładowy kod próbuje się dowiedzieć o imports.To dowiedz się więcej o importach,kod pobiera z typów węzłów Import
I 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)
ten kod przyjmuje nazwę modułu i przechowuje go na liście statystyk.Chociaż kod nie jest wymyślny, pokazuje, jak współdziałać z węzłami AST.
dzięki zdefiniowanej klasie NodeVisitor
możemy jej użyć do analizy drzewa.
analyzer = Analyzer()analyzer.visit(tree)
metoda visit
przeniesie się do twojego visit_<node type>
metoda gdziekolwiek tego typu węzeł jest napotkane podczas przechodzenia przez strukturę drzewa.
Jakie są rodzaje węzłów?Pełną listę można znaleźć w sekcji gramatyki umowy ast
dokumentacji modułu.Prawdę mówiąc, ta dokumentacja jest trochę trudna do wchłonięcia.Możesz odnieść większy sukces, odwołując się do bardziej wyczerpującego przewodnika, takiego jak theGreen Tree Snakes nodes guide.
do tej pory,mam nadzieję, że zrozumiesz jak:
- zbudować AST z kodu źródłowego Pythona.
- wykonaj analizę AST za pomocą
NodeVisitor
.
myślę, że możesz odpowiedzieć na wiele ciekawych pytań dotyczących Twojego kodu używając abstrakcyjnych drzew składniowych.Pytania typu:
- ile zmiennych użyłem?
- jakie są najczęstsze wywołania funkcji w moim kodzie?
- czy moje moduły są ze sobą ściśle powiązane?
- które biblioteki stron trzecich pojawiają się często w różnych pakietach?
modułast
nie jest prawdopodobnie narzędziem, które sięgnie po bardzo often.In te czasy, w których potrzebujesz ast
, jego minimalne API jest dość zapamiętywalne i można szybko analizować kod.
Jeśli uważasz to za przydatne,czy mógłbyś podzielić się tym na Twitterze lub swojej ulubionej witrynie społecznościowej?Lubię rozmawiać z ludźmi o tego rodzaju tematach, możesz mi tweetować na@mblayman.
dowiedz się więcej o Pythonie!
możesz dołączyć do mojego newslettera wraz z 1,000+ innymi programistami, aby pomóc ci dowiedzieć się więcej o Django i Pythonie.