Utrzymuję starą bazę kodu napisaną w Pythonie. W szczególności istnieje złożony fragment kodu, który z modułu wywołuje inne funkcje z innych modułów, które wywołują inne funkcje itd. To nie jest OOP, tylko funkcje i moduły.
Próbowałem śledzić, gdzie zaczyna się i kończy przepływ za każdym razem, gdy wywołuję funkcję main, ale czuję, że muszę narysować Dzieje się tak, ponieważ gubię się w wywołaniach podrzędnych.
Martwi mnie to, że każda funkcja wywołuje wiele funkcji zewnętrznych w swoim ciele, aby ukończyć swoje zadanie i zwrócić wartość do dzwoniącego.
Jak mogę to narysować? Co oznacza, jaki rodzaj wykresu / grafiki byłby odpowiedni do udokumentowania tego rodzaju zachowania / kodu?
Więc nie sądzę, by było przydatne narysowanie diagramu UML , ani schematu blokowego. Może wykres połączeń?
Komentarze
- doxygen – wygeneruje wykres połączenia / dzwoniącego, I ' Nie jestem pewien, jakie ma wsparcie dla Pythona. Wiem, że możesz udokumentować dla niego kod Pythona.
- Ja ' próbowałem użyć Pycallgraph, ale to też ' skomplikowane / zbyt głębokie, aby z niego korzystać. Wynika to ze złożoności mojego kodu, ponieważ łączy on zwykły Python z django i zewnętrznym wywołaniem adresu URL interfejsu API. Dlatego chciałem narysować go ręcznie, biorąc pod uwagę tylko odpowiednią część, której potrzebuję. Problem polega na tym, że nie ' nie wiem, jakiego rodzaju wykresu użyć, aby w pełni zrozumieć system.
- Jeśli to ma pomóc ci go zrozumieć , po prostu narysuj to, co przychodzi naturalnie. Zawsze możesz to uporządkować później, jeśli ' przechodzi do formalnej dokumentacji.
Odpowiedź
Myślę, że to, czego „szukasz, to Diagram sekwencji . Pozwalają one wizualizować kolejność, w jakiej różne moduły wywołują wzajemnie za pomocą strzałek.
Zbudowanie jednej jest proste:
- Narysuj swoją klasę początkową z przerywaną linią poniżej.
- Narysuj następną klasa / metoda w śledzeniu wywołania z kropkowaną linią poniżej
- Połącz linie za pomocą strzałki umieszczonej pionowo pod ostatnią narysowaną strzałką
- Powtórz kroki 2-3 dla wszystkich wywołań w Twoim śladzie
Przykład
Załóżmy, że mamy następujący kod, dla którego chcemy utworzyć diagram sekwencji:
def long_division(quotient, divisor): solution = "" remainder = quotient working = "" while len(remainder) > 0: working += remainder[0] remainder = remainder[1:] multiplier = find_largest_fit(working, divisor) solution += multiplier working = calculate_remainder(working, multiplier, divisor) print solution def calculate_remainder(working, multiplier, divisor): cur_len = len(working) int_rem = int(working) - (int(multiplier) * int (divisor)) return "%*d" % (cur_len, int_rem) def find_largest_fit(quotient, divisor): if int(divisor) == 0: return "0" i = 0 while i <= 10: if (int(divisor) * i) > int(quotient): return str(i - 1) else: i += 1 if __name__ == "__main__": long_division("645", "5")
Pierwszą rzeczą, którą narysujemy, jest punkt wejścia (main
) łączący się z metodą long_division
. Zauważ, że to tworzy prostokąt w long_ podział, oznaczający zakres wywołania metody. W tym prostym przykładzie ramka będzie miała całą wysokość naszego diagramu sekwencji, ponieważ jest to jedyna wykonywana rzecz.
Teraz dzwonimy pod numer find_largest_fit
, aby znaleźć największą wielokrotność mieszczącą się w naszym numerze roboczym i zwraca go do nas. Rysujemy linię od long_division
do find_largest_fit
z innym polem, aby zaznaczyć zakres wywołania funkcji. Zwróć uwagę, jak ramka kończy się, gdy zwracany jest mnożnik; to koniec zakresu funkcji!
Powtórz kilka razy dla większej liczby, a wykres powinien wyglądać mniej więcej tak:
Uwagi
Możesz wybrać, czy chcesz nadawać wywołania etykietami przekazanymi nazwami zmiennych, czy ich wartościami, jeśli chcesz tylko udokumentować jeden konkretny przypadek. Możesz również pokazać rekurencję z funkcją wywołującą samą siebie.
Dodatkowo możesz pokazać tutaj użytkowników, podpowiadać im i łatwo pokazywać ich dane wejściowe do systemu. To „dość elastyczny system, który myślę, że uznasz za raczej użyteczny!
Komentarze
- Dzięki, znam diagram sekwencji, ale wydaje mi się, że bardziej nadaje się do oop. W moim przypadku sytuacja jest nieco bardziej skomplikowana, co oznacza, że na przykład mam około 20 funkcji / pomocników rozsianych po wielu modułach. Jak określiłbym moduł, do którego należy funkcja? Biorąc pod uwagę, że nazwy niektórych funkcji są również zmieniane podczas importu.
- Powiedziałbym, że nie ma znaczenia, ile masz modułów – powyższy przykład też nie jest zły. Po prostu nazwij je, abyś mógł je później znaleźć, ModułA / funkcja1, ModułB / Funkcja2 itd. Dla 20 funkcji będzie większa, ale na pewno nie niemożliwa do zrozumienia. Inną rzeczą, którą możesz zrobić, to zakończyć linię funkcji po jej ostatnim użyciu i umieścić pod nią kolejną linię funkcji, aby zaoszczędzić miejsce w poziomie na diagramie.
Odpowiedź
Myślę, że wykres połączeń byłby najwłaściwszą wizualizacją.Jeśli zdecydujesz się nie robić tego ręcznie, dostępne jest ładne małe narzędzie o nazwie pyan
, które przeprowadza statyczną analizę pliku w języku Python i może generować wizualizowany wykres połączeń za pomocą kropki graphviz plik (który można wyrenderować na obrazie). Było kilka rozwidleń, ale wydaje się, że najbardziej w pełni funkcjonalny jest https://github.com/davidfraser/pyan .
Po uruchomieniu polecenia wystarczy określić wszystkie pliki, które mają zostać przetworzone:
python ~/bin/pyan.py --dot a.py b.py c.py -n > pyan.dot; dot -Tpng -opyan.png pyan.dot
lub
python ~/bin/pyan.py --dot $(find . -name "*.py") -n > pyan.dot; dot -Tpng -opyan.png pyan.dot
Możesz uczynić wykres czystszym za pomocą „-n”, które usuwa wiersze pokazujące, gdzie funkcja została zdefiniowana.