Depuração (Debugging)

Diferentes tipos de erros pode ocorrer em um programa. É importante diferenciarmos os diferentes tipos de erro para corrigi-los mais rapidamente.

  1. Erros de sintaxe (syntax errors) ocorrem quando o Python está traduzindo o código fonte do seu programa em código executável (byte code). Eles usualmente indicam que você escreveu algo sintaticamente errado no seu programa. Por exemplo, não escrever : no final da linha linha com um def produz uma mensagem de certa forma redundante SyntaxError: invalid syntax.

  2. Erros de execução (runtime erros) ocorrem durante a execução do programa. Eles usualmente indicam que o você cometeu um erro de lógica no seu programa. A maioria das mensagens que relatam um erro de execução indicam o ponto do programa em que o erro ocorreu e quais funções estavam sendo executadas. Por exemplo, recursão infinita causa o erro de execução maximum recursion depth exceeded. É importante observar que apesar do erro ter se manifestado em um certo ponto durante a execução do programa o erro de lógica responsável pelo erro pode estar em um trecho de código bem distante desse ponto.

  3. Erros de semântica (semantic errors) ocorrem quando o programa não apresenta erro de sintaxe e não ocorre um erro durante a execução. O programa é executado do começo ao fim, mas ele não produz o resultado desejado. Isto indica que você cometeu um erro de lógica no seu programa. Por exemplo, uma expressão pode não ser avaliada na ordem que você deseja e isso produz o resultado errado. Por exemplo, desejamos calcular o valor da expressão a / (b * c) mas escrevemos no programa a / b * c que devido a precedência dos operadores é avaliada como (a / b) * c.

O primeiro passo em depurar (debugging), ou seja eliminar erros de programação, é identificar que tipo de erro você cometeu. Apesar das próximas seções estarem organizadas de acordo com o tipo do erro, alguma técnicas são aplicáveis em mai do que uma situação.

Erros de sintaxe (Syntax errors)

Erros de sintaxe são comumente fáceis de serem corrigidos, uma vez que você entenda o qual é o problema. Infelizmente, as mensagens de erro frequentemente não ajudam muito, em particular, os iniciantes. A mensagem mais comum é SyntaxError: invalid syntax e SyntaxError: invalid token, e nenhuma delas é muito informativa.

Por outro lado, as mensagens indicam onde no programa o erro ocorreu. Na verdade, ele diz a você onde o Python notou o problema, que não é necessariamente onde está o erro. Algumas vezes o erro está localizado em um ponto anterior ao indicado pela mensagem de erro, frequentemente na linha anterior.

Se você está construindo o programa incrementalmente, pouco a pouco, você deveria ter um boa ideia sobre onde está localizado o erro. Estará na última linha que você adicionou.

Se você está copiando o código de um livro. comece comparando o seu código com o código original cuidadosamente. Verifique caractere por caractere. Ao mesmo tempo, o código original pode estar errado, assim se você vir algo que se parece com um erro de sintaxe, ele pode ser um erro mesmo.

Aqui estão alguma maneiras de se evitar os erros de sintaxe mais comuns.

  1. Certifique-se que você não está usando uma palavra reservada do Python (Python keyword) como nome de variável.

  2. Verifique que você colocou um dois-pontos (:) no final do cabeçalho de cada comando composto (compound statement), incluindo laço for, laço while, comandos de seleção if, elif e else e declaração de funções def.

  3. Verifique se a tabulação é consistente. Você pode tabular com espaços ou tabs mas não é aconselhável misturá-los, Cada nível deve ter a mesma quantidade de espaços ou tabs.

  4. Certifique de que as aspas ou apóstrofos de qualquer string no código estão emparelhados.

  5. Se você tem strings que se estendem por várias linhas delimitados por citação tripla (com ' ou "), certifique-se que você terminou o strings de maneira apropriada. Um string não terminado pode causar o erro invalid token no final do seu programa ou pode tratar o trecho seguinte de código como um string até encontrar o próximo string. No segundo caso pode até não produzir uma mensagem de erro!

  6. Uma falta de fecha parêntese, chave ou colchete – (, { ou [ – faz o Python continuar com a próxima linha como parte da expressão corrente. Geralmente, um erro ocorre quase que imediatamente na próxima linha.

  7. Verifique pelo clássico = em vez de == em uma condição.

Se nada funcionar, vá para a próxima seção…

Não consigo fazer o meu programa rodar

Se o compilador diz que existe um erro e você não consegue vê-lo, isto pode ser porque você e o compilador não estão olhando para o mesmo código. Verifique o seu ambiente de programação para ter certeza que o programa que você está editando é o mesmo que o Python está tentando executar. Se você não tem certeza tente colocar deliberadamente um erro de sintaxe óbvio no início do programa. Agora execute-o ou importe-o novamente. Se o compilador não encontrar o novo erro, há provavelmente algo errado com a maneira que o seu ambiente está configurado.

Se isso ocorrer, uma estratégia é começar novamente um novo programa como “Olá, Mundo!”, e se certificar que você consegue far um programa conhecido ser executado. Então, gradualmente, adicione pedaços do seu programa original ao novo programa fazendo funcionar depois de cada alteração.

Erros de execução (Runtime errors)

Uma vez que seu programa esteja sintaticamente correto, Python pode importá-lo e pelo menos começar a executá-lo. O que pode dar errado agora?

Meu programa não faz nada

Este problema é muito comum quando o seu arquivo consiste apenas de funções ou classe mas na realidade não há invocação de alguma função para se começar a execução. Isto pode ser intencional se você somente planeja importar esse módulo para suprir classes e funções.

Se isso não é intecional, certifique-se que você está invocando uma função para iniciar a execução, ou execute uma função a partir do Python shell. Também leia a seção Fluxo de execução mais abaixo.

Meu programa trava

Se um programa para e parece que não está fazendo nada, dizemos que ele está travado (hanging). Frequentemente isso significa que você cometeu algum erro de lógica e que ele está em um laço infinito (infinite loop) ou uma recursão infinita.

  1. Se existe um laço particular que você suspeita é o problema, acrescente um comando print imediatamente antes do laço que diz entrando no laço e um outro imediatamente depois que diz sai do laço.

  2. Execute o programa. Se o programa exibe a primeira mensagem e não exibe a segunda, você encontrou um laço infinito. Vá para a seção Laço infinito mais adiante.

  3. Na maioria da vezes, um recursão infinita fará com que o programa seja executado até que um erro RuntimeError: Maximum recursion depth exceeded error seja produzido. Se isso ocorre, vá para a seção Recursão infinita mais abaixo.

  4. Se você não está recebendo a mensagem anterior e suspeita que existe um problema com o método da recursão ou função, você ainda pode usar a técnica na seção Recursão infinita.

  5. Se nenhum desses passos funcionar, começa a testar outros laços e outras funções recursivas e métodos.

  6. Se isto não funcionar, então é possível que você não esteja entendo o fluxo de execução do seu programa. Vá para a seção Fluxo de execução mais adiante.

Laço infinito

Se você encontrou um laço infinito e você acredita que sabe qual laço está causando o problema, acrescente um print no final do laço que exibe o valor de cada variável na condição e o valor da condição do laço.

Por exemplo:

while x > 0 and y < 0:
    # faz algo com x
    # faz algo com y

    print("x: ", x)
    print("y: ", y)
    print("condicao: ", (x > 0 and y < 0))

Agora, quando você executa o programa, você verá três linha na saída para cada vez que o laço é executado. Na última vez que o laço é executado a condição deveria ser False. Se o laço não para você será capaz de ver os valores de x``e ``y, e você pode ser capaz de perceber porque os valores não estão sendo atualizados corretamente.

Em um ambiente de desenvolvimento como PyScripter, podemos também colocar um ponto de pausa (breakpoint) no início do laço e para a execução do programa cada vez que no fluxo de execução esse ponto é atingido. Nesse momento é possível inspecionar o valores de x e y movendo seu curso sobre essas variáveis.

É evidente que todo processo de programação e depuração exige que você tenha um bom modelo mental do que o algoritmo deve estar fazendo: se você não entende o que deve acontecer com x e y, imprimir e inspecionar os valores dessas variáveis será de pouquíssima valia. Provavelmente o melhor para depurar o código é longe do seu computador, trabalhando a sua compreensão do que deveria estar acontecendo.

Recursão infinita

Na maioria das vezes, um recursão infinita causará que o programa seja executado até que a mensagem de erro Maximum recursion depth exceeded seja exibida pelo Python.

Se você suspeita que uma função ou método está causando uma recursão infinita, comece certificando-se que a recursão tem uma base. Em outras palavras, deve existir alguma condição que causa a função ou método a retornar sem fazer mais chamadas recursivas. Se não há uma base você precisa repensar o algoritmo e identificar um caso base.

Se existe um caso base mas programa parece não chegar nesse caso, acrescente um print no início da função ou método para exibir os valores dos parâmetros. Agora quando executar o programa, você verá na saída algumas linhas cada vez que a função ou método for invocado e você verá os valores dos parâmetros. Se os parâmetros não estão se aproximando do caso base, você começará ater ideias sobre a razão disso estar acontecendo.

Mais uma vez, se você tem um ambiente que permite pontos de pausa e um passo-a-passo da execução do programa, aprenda como usar esses recursos apropriadamente. É nossa opinião que andar pelo o código passo-a-passo cria o melhor e mais preciso modelo de como a computação acontece, Use isso se você tiver.

Fluxo de execução

Se você não está seguro sobre como o fluxo de execução (flow of execution) esta se movimento através do seu programa, acrescente alguns comandos print no inícios de cada função com uma mensagem como entrei na função blá onde blá é o nome da função. No final de cada função, antes que o fluxo de execução volte a função que fez a chamada desta, acrescente um comandos print com a mensagem sai da função blá.

Quando executo o programa ocorre uma excessão

Se algo errado ocorre durante a execução do programa, Python imprime uma mensagem que inclui o nome da exceção (exception), a linha do programa onde o problema ocorreu e um rastro (traceback) dos passos que levaram até aquele ponto do programa.

Coloque pontos de pausa na linha que está causando a exceção, e olhe no código a sua volta.

O rastro (traceback) identifica a função que está sendo executada, e a função que a invocou, a então a função que invocou a função que a invocou, e assim sucessivamente até chegar na função inicial do programa. Em outras palavras, o Python exibe os rastro das chamadas de função que fez com que o fluxo de execução chegasse aquela determinada linha. Ele também inclui o número da linha no programa de onde essas chamadas foram feitas.

O primeiro passo examinar o lugar do programa onde o erro ocorreu e ver se você consegue perceber o que ocorreu. A seguir estão alguns do erros de execução mais comuns:

NameError

Você está tentando usar uma variável que não existe no ambiente corrente. Lembre-se que variáveis locais são locais. Você não pode fazer referência a elas fora da função onde foram definidas.

TypeError

Existem várias causa possíveis:

  1. Você esta tentando usar um valor inapropriado. Por exemplo, índices de strings, listas e tuplas devem ser valores inteiros (tipo int).

  2. Existe uma incompatibilidade entre itens em um formato de um string e os valores passados para conversão. Isto ocorre quando o número de itens não é o esperado ou uma conversão inválida é executada.

  3. Você está passando o numero errado de argumentos para uma função ou método. Para métodos, olhe a definição do método e verifique se o primeiro parâmetros é self. Depois olhe a invocação do método; certifique-se de que a chamada do método sobre um objeto do tipo certo e fornecendo os argumento corretamente.

KeyError

Você esta tentando acessar um elemento de um dicionário usando um valor de uma chave que o dicionário não contém.

AttributeError

Você esta tentando acessar um atributo ou método que não existe

IndexError

O índice que você está usando para acessar uma lista, string, ou tupla é maior que o seu comprimento menos um. Imediatamente antes da linha desse erro acrescente um comando print para exibir o valor do índice e o comprimento da lista, string ou tupla. O objetos tem o tamanho certo? O índice é um valor correto?

Coloquei tanto print no meu programa que não consigo ver nada

Um dos problemas em usar comandos print para depuração é que o programa pode acabar exibindo tanto valor de variável ou mensagem que se torna difícil enxergar qualquer coisa. Existem duas maneiras de se proceder: simplifique a saída ou simplifique o programa.

Para simplificar a saída do programa, você pode remover ou comentar alguns comandos print que não estão ajudando, ou combinar eles, ou formatar a saída de tale maneira que facilite a compreensão das mensagens.

Para simplificar o programa existem várias coisas que você pode fazer. Primeiro reduza o tamanho da entrada com o qual o programa está trabalhando. Por exemplo, se você está ordenando os elemento de uma lista, ordene os elementos de uma lista menor. Se o programa recebe os dados que um usuário está digitante, digite o menor número de valores que fazem com que ocorra um erro no programa.

Segundo, limpe o programa. Remova código que não está sendo usado e reorganize o programa de modo a fazê-lo o mais legível possível. Por exemplo, se você suspeita que o erro está em uma parte muita aninhada do programa (por exemplo, em um while dentro de um while dentro de um for …), tente reescrever essa parte com estruturas mais simples. Se você suspeita que o erro está em uma função muito longa, tente quebrar a função em funções menores e testar cada uma separadamente.

Frequentemente o processo de encontra um teste minimal que faz com que o problema se manifeste acaba conduzindo-nos ao ponto do programa em que está o erro. Se você descobre que o programa funciona em uma situação, mas não funciona em outra, isso pode dar uma dica de onde está o programa.

Similarmente, reescrever um pedaço de código pode ajudá-lo a encontra um erro sútil. Se você faz uma lateração que você acredita que não deveria afetar a execução do programa e ela afeta, isso pode lhe alertar sobre algo.

Você também pode envolver os seus comandos print para depuração em alguma condição, assim você poderá se concentrar em examinar as mensagens de depuração que realmente interessam. Por exemplo, se você está tentando encontrar um elemento em uma lista ordenada com busca binária (binary search), e o programa está com algum erro, você pode colocar um comando print para depuração dentro de uma condição: se a sub-lista que o programa está examinando tem menos de 6 elementos, imprima informação para depuração, em caso contrário não imprima coisa alguma.

Similarmente, pontos de pausa pode ser feito condicionalmente: você pode definir o ponto de pausa em um comando e editar a sua configuração para que “somente pause neste ponto se uma dada expressão seja verdadeira”.

Erros semânticos

De certa forma, erros semânticos (semantic erros) são os mais difíceis de serem corrigidos pois nem o compilador e nem o interpretador Python fornecem alguma informações sobre o que está errado. Somente você sabe qual é o comportamento esperado do programa, somente você sabe que ele não está fazendo o que deveria.

O primeiro passo é fazer uma conexão entre o programa e o comportamento que você está vendo. Você precisa sobre o que o programa está de fato fazendo. Uma coisa que torna isso difícil é que programas são executados muito rapidamente. Outra dificuldade é que programas fazem o que ordenamos que façam e não o que gostaríamos que fizessem.

Você frequentemente gostaria de desacelerar o seu programa para a velocidade de humanos, e como alguns programas para depuração (debuggers) você pode. Mas o tempo que é gasto para colocar alguns comandos print em lugares estratégicos é frequentemente mais curto comparado com o tempo gasto para preparar o debugger, inserir e remover pontos de pausa (breakpoints) e acompanhar a execução do programa até que o erro ocorra.

Meu programa não funciona

Você deve se fazer as seguintes perguntas:

  1. Há alguma coisa que o programa deveria estar fazendo mas não está? Encontra a seção do seu código que é responsável essa função e certifique-se de que está fazendo o que você acredita que deveria estar.

  2. Há alguma coisa que está ocorrendo mas não deveria? Encontre no seu código a seção que é responsável por essa tarefa e veja se algo está sendo feito quando não deveria.

  3. Há uma seção do código que está produzindo um efeito que não é o que você espera? Certifique-se que você entende o trecho de código em questão, especialmente se envolve a invocação a funções ou métodos de outros módulos. Leia a documentação das funções que você esta invocando. Experimente-as através de testes simples e verifique os resultados produzidos. Para isso algumas vezes é útil utilizarmos o Python shell.

Para programar você deve ter um modelo mental de como programas trabalham. Se você escreve um programa que não faz o que você espera, frequentemente (sempre?) o problema não é o programa, mas sim o seu modelo mental.

A melhor maneira para corrigir o seu modelo mental é quebrar o programa em componentes (usualmente funções e métodos) e testar cada componente independentemente. Uma vez que você encontra discrepância entre o seu modelo e a realidade, você pode resolver o problema.

É claro que, você deveria construir e testar componentes a medida que você desenvolve o programa. Se você encontrar um problema, deveria haver apenas um parte pequena de novo código que não temos certeza que está correto.

Minha expressão cabeluda não faz o que eu esperava

Escrever expressões complexas não é ruim contanto que elas seja razoavelmente claras e legíveis, mas elas podem ser difíceis de serem depuradas. É frequentemente uma boa ideia quebrar um expressão complexa em um série de atribuições a variáveis temporárias.

Por exemplo:

self.hands[i].addCard (self.hands[self.findNeighbor(i)].popCard())

Pode ser reescrito como:

neighbor = self.findNeighbor (i)
pickedCard = self.hands[neighbor].popCard()
self.hands[i].addCard (pickedCard)

A versão explícita é mais legível pois os nome das variáveis fornece documentação adicional, e é mais fácil de ser depurada já que você pode verificar o tipo das variáveis intermediárias e exibir ou inspecionar os seu valores.

Outro problema que pode ocorrer com expressões grandes é que a ordem que ele é avaliada pode não ser o que você gostaria. Por exemplo, se você traduz a expressão x/2pi para Python, você pode escrever:

y = x / 2 * math.pi;

Isto não está correto pois multiplicação e divisão tem a mesma precedência e são avaliada da esquerda para a direita. Assim, essa expressão computa (x/2)pi.

Uma boa maneira de depurar expressões é adicionar parênteses para fazer a ordem da avaliação explícita:

y = x / (2 * math.pi);

Sempre que você não tem certeza sobre a ordem da avaliação, use parênteses. Além do programa estar correto (no sentido que ele faz o que você desejava), ele também será mais legível para outras pessoas que não memorizaram as regras de precedência.

Minha função ou método não retorna o que eu espero

Se você tem um comando return com uma função complexa, você não tem a chance de imprimir o valor antes dele ser retornado. Novamente, você pode usar variáveis temporárias. Por exemplo, em vez de:

return self.hands[i].removeMatches()

você pode escrever:

count = self.hands[i].removeMatches()
return count

Agora, você tem a oportunidade de exibir e inspecionar o valor de count antes de retorna-lo.

Eu estou realmente travado e necessito ajuda

Primeiro, tente ficar loge do computador por alguns minutos. Computadores emitem ondas que afetam o seu célebro, causando os seguintes efeitos:

  1. Frustação e/ou ódio.

  2. Crenças superticiosas (o computador me odeia) e pensamentos mágicos (o programa só funciona quando uso meu chapéu preto).

  3. Programação com método passei aletório (tentativa de programa escrevendo todo os programa possívesi a escolhendo o que faz o que deve).

  4. Mais crenças superticiosas (na minha casa funciona).

Se você estiver sofrendo de qualquer um desses sintomas, levantesse e vá andar. Quando você estiver calmo, pense sobre o programa. O que ele está fazendo? O que possivelmente pode causar esse comportamento? Quando foi a última vez que o programa parecia estar funcionando e o que você alterou?

Não, eu realmente preciso de ajuda

Isso acontece. Mesmo os melhores programadores as vezes ficam travados em um problema. Algumas vezes você trabalha tanto tempo em um programa que você não consegue ver o seu erro. O para fresco de olhos pode ajudar.

Antes de você trazer outra pessoa para olhar o seu programa, certifique-se que você exauriu as técnicas descritas aqui. O seu programa deve ser o mais simples possível e você deve estar trabalhando com a menor entrada que causa o erro. Você deve ter colocado alguns comandos print em lugares estratégicos para exibir valores intermediários (a saída que eles produzem deve ser compreensível). Você deve entender o problema suficientemente para descrevê-lo sucintamente.

Quando você traz alguém para ajudar, certifique-se em dar as informações que elas necessitam:

  1. Se há uma mensagem de erro, que mensagem é essa e qual a parte do programa que ela indica?

  2. Qual foi a última coisa que você fez antes desse erre ocorrer? Quais foram as últimas linhas de código que você escreveu ou qual é o novo teste onde o programa falha?

  3. O que você tentou até agora, e o que você aprendeu?

Bons instrutores e monitores farão algo que não deveria ofendê-lo. eles não acreditarão no que você diz a eles “Estou certo de que todas as rotinas de entrada estão funcionando e que eu forneci os dados corretamente!”. Eles desejarão verificar tudo pessoalmente. Afinal de contas, o seu programa tem um erro. A sua compreensão e inspeção do código não encontrou o erro ainda. Logo, você deveria esperar ter as suas convicções desafiadas. A medida que você ganhar prática para ajudar outros, você necessitará fazer a mesma coisa.

Quando você encontrar o erro (ou bug), gaste alguns momentos pensando no que você poderia ter feito para encontrá-lo mais rapidamente. Na próxima vez que você ver algo similar, você será capas de encontrar o bug mais rapidamente.

Lembre-se, o objetivo não é apenas fazer o programa funcionar. O objetivo e aprender como fazer programas corretos.

You have attempted of activities on this page