Disegna un grafico di chiamata


11

Sto mantenendo una vecchia base di codice scritta in Python. In particolare esiste un codice complesso che da un modulo chiama altre funzioni da altri moduli che chiamano altre funzioni e così via. Non è OOP, solo funzioni e moduli.
Ho cercato di tenere traccia di dove inizia e termina il flusso ogni volta che chiamo la funzione principale, ma sento che devo disegnarlo perché mi perdo nelle chiamate secondarie.

Ciò che mi preoccupa è che ogni funzione chiama più funzioni esterne all'interno del loro corpo per completare il loro compito e restituire il valore al chiamante.

Come posso disegnarlo? Significa che tipo di grafico / grafico sarebbe appropriato per documentare questo tipo di comportamento / codice?

Quindi, non penso che sarebbe utile disegnare un diagramma UML, né un diagramma di flusso. Un grafico di chiamata, forse?


doxygen - genererà grafici di chiamata / chiamante, non sono sicuro di quanto supporto abbia per Python. So che puoi documentare il codice Python per questo.
gbjbaanb,

Ho provato il pycallgraph ma è troppo complicato / troppo profondo per usarlo. Ciò è dovuto alla complessità del mio codice perché mescola Python semplice con Django e chiamata esterna all'URL dell'API. Ecco perché volevo disegnarlo a mano tenendo conto solo della parte rilevante di cui ho bisogno. Il problema è che non so che tipo di grafico utilizzare per avere una piena comprensione del sistema
Leonardo

5
Se questo è solo per aiutarti a capirlo, basta disegnare tutto ciò che viene naturale. Puoi sempre riordinarlo più tardi se va nella documentazione formale.
jonrsharpe,

Risposte:


9

Penso che quello che stai cercando qui sia un diagramma di sequenza . Questi consentono di visualizzare l'ordine in cui i vari moduli si chiamano tramite le frecce.

Costruire uno è semplice:

  1. Disegna la tua classe iniziale con una linea tratteggiata sotto di essa.
  2. Disegna la successiva classe / metodo nella traccia della chiamata con una linea tratteggiata al di sotto di essa
  3. Collega le linee con una freccia, posizionata verticalmente sotto l'ultima freccia che hai disegnato
  4. Ripeti i passaggi 2-3 per tutte le chiamate nella tua traccia

Esempio

Supponiamo di avere il seguente codice per cui vogliamo creare un diagramma di sequenza per:

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")

La prima cosa che disegneremo è il punto di ingresso ( main) che si collega al metodo long_division. Si noti che ciò crea una casella in long_division, che indica l'ambito della chiamata del metodo. Per questo semplice esempio, la casella sarà l'intera altezza del nostro diagramma di sequenza a causa del fatto che questa è l'unica cosa eseguita.

inserisci qui la descrizione dell'immagine

Ora chiamiamo find_largest_fitper trovare il più grande multiplo che si adatta al nostro numero di lavoro e ce lo restituisce. Tracciamo una linea da long_divisiona find_largest_fitcon un'altra casella per indicare l'ambito per la chiamata di funzione. Nota come termina la casella quando viene restituito il moltiplicatore; questa è la fine di quell'ambito di funzioni!

inserisci qui la descrizione dell'immagine

Ripeti alcune volte per un numero maggiore e il tuo grafico dovrebbe assomigliare a questo:

inserisci qui la descrizione dell'immagine

Appunti

È possibile scegliere se si desidera etichettare le chiamate con i nomi delle variabili passati o i loro valori se si desidera documentare solo un caso specifico. Puoi anche mostrare la ricorsione con una funzione che si chiama da sola.

Inoltre, puoi mostrare agli utenti qui e chiedere loro e mostrare i loro input nel sistema abbastanza facilmente. È un sistema abbastanza flessibile che penso che troverai piuttosto utile!


Grazie, conosco il diagramma di sequenza, ma mi sembra che sia più adatto per oop. Nel mio caso le cose sono un po 'più disordinate, nel senso che ad esempio ho circa 20 funzioni / helper distribuite su più moduli. Vorrei specificare il modulo a cui appartiene la funzione? Considerando che alcune funzioni vengono anche rinominate durante le importazioni ..
Leonardo

1
Direi che non importa quanti moduli hai - l'esempio sopra non è affatto oop. Basta nominarli in modo da poterli trovare in seguito, ModuleA / function1, ModuleB / Function2 ecc. Per 20 funzioni sarà più grande, ma sicuramente non impossibile da capire. Un'altra cosa che puoi fare è terminare la linea per una funzione dopo il suo ultimo utilizzo e mettere un'altra linea di funzioni al di sotto di essa per risparmiare spazio orizzontale nel diagramma.
Appunto

5

Penso che un grafico di chiamata sarebbe la visualizzazione più appropriata. Se decidi di non farlo a mano, c'è un simpatico piccolo strumento chiamato pyanche esegue analisi statiche su un file Python e può generare un grafico di chiamata visualizzato tramite un file dot graphviz (che può essere renderizzato su un'immagine). Ci sono state un paio di forchette, ma la più completa sembra essere https://github.com/davidfraser/pyan .

Devi solo specificare tutti i file che vuoi elaborare quando esegui il comando:

python ~/bin/pyan.py --dot a.py b.py c.py -n > pyan.dot; dot -Tpng -opyan.png pyan.dot

o

python ~/bin/pyan.py --dot $(find . -name '*.py') -n > pyan.dot; dot -Tpng -opyan.png pyan.dot

Puoi rendere il grafico più pulito con '-n' che rimuove le linee che mostrano dove è stata definita una funzione.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.