Narysuj wykres połączeń


12

Utrzymuję starą bazę kodu napisaną w języku python. 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 i tak dalej. 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ę główną funkcję, ale czuję, że muszę to narysować, ponieważ gubię się w wywołaniach podrzędnych.

To, co mnie martwi, to to, że każda funkcja wywołuje wiele funkcji zewnętrznych w swoim ciele, aby zakończyć zadanie i zwrócić wartość wywołującemu.

Jak mogę to narysować? Czyli jaki rodzaj wykresu / grafiki byłby odpowiedni do udokumentowania tego rodzaju zachowania / kodu?

Nie sądzę więc, aby rysowanie diagramu UML, ani schematu blokowego było przydatne. Może wykres połączeń?


doxygen - wygeneruje wykresy wywołań / wywołań, nie jestem pewien, jakie wsparcie ma dla Pythona. Wiem, że możesz udokumentować dla niego kod Pythona.
gbjbaanb

Próbowałem pycallgraph, ale jest to zbyt skomplikowane / zbyt głębokie, aby go użyć. Wynika to ze złożoności mojego kodu, ponieważ łączy on zwykły python z django i zewnętrznym wywołaniem adresu URL API. Dlatego chciałem to narysować ręcznie, biorąc pod uwagę tylko odpowiednią część, której potrzebuję. Problem polega na tym, że nie wiem, jakiego rodzaju wykresu użyć, aby w pełni zrozumieć system
Leonardo

5
Jeśli ma to tylko pomóc ci to zrozumieć, po prostu narysuj wszystko, co przychodzi naturalnie. Zawsze możesz to uporządkować później, jeśli przejdzie do formalnej dokumentacji.
jonrsharpe

Odpowiedzi:


9

Myślę, że tutaj szukasz diagramu sekwencji . Umożliwiają one wizualizację kolejności, w której różne moduły wywołują się nawzajem za pomocą strzałek.

Konstruowanie jednego jest proste:

  1. Narysuj klasę początkową z kropkowaną linią poniżej.
  2. Narysuj następną klasę / metodę w ścieżce wywołania, pod nią kropkowaną linię
  3. Połącz linie ze strzałką ustawioną pionowo poniżej ostatniej narysowanej strzałki
  4. Powtórz kroki 2-3 dla wszystkich połączeń w swoim ś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")

Najpierw narysujemy punkt wejścia ( main) łączący się z metodą long_division. Zauważ, że tworzy to pole w long_division, oznaczające zakres wywołania metody. W tym prostym przykładzie ramka będzie miała całą wysokość naszego schematu sekwencji, ponieważ jest to jedyna rzecz, którą można uruchomić.

wprowadź opis zdjęcia tutaj

Teraz dzwonimy, find_largest_fitaby znaleźć największą wielokrotność, która mieści się w naszym numerze roboczym i zwraca go nam. Rysujemy linię od long_divisiondo find_largest_fitz innym polem, aby zaznaczyć zakres wywołania funkcji. Zwróć uwagę, jak pole kończy się po zwróceniu mnożnika; to koniec zakresu funkcji!

wprowadź opis zdjęcia tutaj

Powtórz kilka razy dla większej liczby, a twój wykres powinien wyglądać mniej więcej tak:

wprowadź opis zdjęcia tutaj

Notatki

Możesz wybrać, czy chcesz oznaczać połączenia przekazanymi nazwami zmiennych, czy ich wartości, jeśli chcesz udokumentować tylko jeden konkretny przypadek. Możesz także wyświetlić rekurencję z funkcją wywołującą samą siebie.

Ponadto możesz tutaj wyświetlać użytkowników, monitować ich i dość łatwo wprowadzać dane do systemu. To dość elastyczny system, który wydaje mi się przydatny!


Dzięki, znam schemat sekwencji, ale wydaje mi się, że jest bardziej odpowiedni dla oop. W moim przypadku sprawy są nieco bardziej niechlujne, co oznacza, że ​​na przykład mam około 20 funkcji / pomocników rozmieszczonych w wielu modułach. Czy powinienem określić moduł, do którego należy funkcja? Biorąc pod uwagę, że niektóre funkcje są również zmieniane podczas importu.
Leonardo

1
Powiedziałbym, że nie ma znaczenia, ile masz modułów - powyższy przykład w ogóle też nie działa. Wystarczy je nazwać, aby można je było później znaleźć: moduł A / funkcja 1, moduł B / funkcja 2 itd. Dla 20 funkcji będzie on większy, ale na pewno nie niemożliwy do zrozumienia. Kolejną rzeczą, którą możesz zrobić, to zakończyć linię dla funkcji po jej ostatnim użyciu i umieścić pod nią kolejną linię funkcji, aby zaoszczędzić miejsce na diagramie.
Ampt

6

Myślę, że wykres połączeń byłby najbardziej odpowiednią wizualizacją. Jeśli zdecydujesz się nie robić tego ręcznie, istnieje ładne małe narzędzie o nazwie, pyanktóre wykonuje analizę statyczną pliku Pythona i może wygenerować wizualizowany wykres wywołania za pomocą pliku kropki graphviz (który może być renderowany na obraz). Było kilka rozwidleń, ale najbardziej w pełni funkcjonalny wydaje się być https://github.com/davidfraser/pyan .

Musisz tylko określić wszystkie pliki, które chcesz przetworzyć po uruchomieniu polecenia:

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óry usuwa linie pokazujące, gdzie zdefiniowano funkcję.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.