Estou mantendo uma base de código antiga escrita em python. Em particular, há um trecho complexo de código que a partir de um módulo chama outras funções de outros módulos que chamam outras funções e assim por diante. Não é OOP, apenas funções e módulos.
Eu tentei acompanhar onde o fluxo começa e termina sempre que chamo a função principal, mas sinto que preciso desenhar isso porque estou me perdendo nas subchamadas.
O que me preocupa é que cada função chama várias funções externas dentro de seu corpo para completar sua tarefa e retornar o valor ao chamador.
Como posso desenhar isso? O que significa que tipo de gráfico / gráfico seria apropriado para documentar esse tipo de comportamento / código?
Então, não acho que seria útil desenhar um diagrama UML , nem um fluxograma. Um gráfico de chamadas, talvez?
Comentários
- doxygen – gerará gráficos de chamadas / chamadores, I ' Não tenho certeza de quanto suporte ele tem para python. Eu sei que você pode documentar o código Python para ele.
- Eu ' tentei o pycallgraph, mas ' também complicado / muito profundo para usá-lo. Isso se deve à complexidade do meu código, pois ele mistura python simples com django e chamada externa para URL da API. É por isso que quis desenhá-lo à mão levando em consideração apenas a parte relevante de que preciso. O problema é que eu não ' não sei que tipo de gráfico usar para ter um entendimento completo do sistema
- Se for apenas para ajudá-lo a entendê-lo , apenas desenhe o que vier naturalmente. Você sempre pode arrumar mais tarde se ' estiver indo para a documentação formal.
Resposta
Acho que o que você está procurando aqui é um Diagrama de sequência . Eles permitem que você visualize a ordem em que vários módulos chamam um ao outro através do uso de setas.
Construir uma é simples:
- Desenhe sua classe inicial com uma linha pontilhada abaixo dela.
- Desenhe a próxima classe / método no rastreamento de chamada com uma linha pontilhada abaixo dela
- Conecte as linhas com uma seta, posicionada verticalmente abaixo da última seta que você desenhou
- Repita as etapas 2-3 para todas as chamadas em seu rastreamento
Exemplo
Vamos supor que temos o seguinte código para o qual queremos criar um diagrama de sequência:
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")
A primeira coisa que desenharemos é o ponto de entrada (main
) conectando-se ao método long_division
. Observe que isso cria uma caixa em long_ divisão, significando o escopo da chamada do método. Para este exemplo simples, a caixa terá toda a altura do nosso diagrama de sequência devido ao fato de que esta é a única coisa executada.
Agora chamamos find_largest_fit
para encontrar o maior múltiplo que se encaixe em nosso número de trabalho , e nos devolve. Desenhamos uma linha de long_division
a find_largest_fit
com outra caixa para indicar o escopo da chamada de função. Observe como a caixa termina quando o multiplicador é retornado; este é o fim do escopo das funções!
Repita algumas vezes para um número maior e seu gráfico deve ser semelhante a este:
Notas
Você pode escolher se deseja rotular as chamadas com os nomes de variáveis passados ou seus valores, se apenas desejar documentar um caso específico. Você também pode mostrar a recursão com uma função chamando a si mesma.
Além disso, você pode mostrar os usuários aqui e solicitá-los e mostrar sua entrada no sistema com bastante facilidade. É um sistema bastante flexível que acho que você achará bastante útil!
Comentários
- Obrigado, eu conheço o diagrama de sequência, mas parece-me que é mais adequado para oop. No meu caso, as coisas estão um pouco mais confusas, o que significa que, por exemplo, tenho cerca de 20 funções / auxiliares espalhados por vários módulos. Como eu especificaria o módulo ao qual a função pertence? Considerando que algumas funções também são renomeadas durante as importações …
- Eu diria que não importa quantos módulos você tem – o exemplo acima também não é oop. Apenas nomeie-os para que você possa encontrá-los mais tarde, MóduloA / função1, MóduloB / Função2 etc. Para 20 funções, será maior, mas definitivamente não impossível de entender. Outra coisa que você pode fazer é terminar a linha de uma função após seu último uso e colocar outra linha de funções abaixo dela para economizar espaço horizontal em seu diagrama.
Resposta
Acho que um gráfico de chamadas seria a visualização mais apropriada.Se você decidir não fazer isso manualmente, existe uma pequena ferramenta agradável chamada pyan
que faz análise estática em um arquivo python e pode gerar um gráfico de chamada visualizado por meio de um ponto graphviz (que pode ser renderizado em uma imagem). Existem alguns garfos, mas o mais completo parece ser https://github.com/davidfraser/pyan .
Você só precisa especificar todos os arquivos que deseja processar ao executar o comando:
python ~/bin/pyan.py --dot a.py b.py c.py -n > pyan.dot; dot -Tpng -opyan.png pyan.dot
ou
python ~/bin/pyan.py --dot $(find . -name "*.py") -n > pyan.dot; dot -Tpng -opyan.png pyan.dot
Você pode tornar o gráfico mais limpo com o “-n” que remove o linhas mostrando onde uma função foi definida.