Python Depuração

Há erros que o Python mostra na hora, com traceback e tudo. E há os piores: o programa roda até o fim, sem reclamar, e dá a resposta errada. O carrinho da lojinha da Ana cobra R$ 19,99 por uma compra de quase cem reais, e nenhuma mensagem diz por quê. Achar a causa de um defeito assim se chama depurar (em inglês, debugging: "tirar os insetos", porque defeito de programa tem o apelido de bug, "inseto"). Nesta aula você vai depurar de dois jeitos: com print e com o depurador do Python, o pdb.

Você vai aprender
  • A ideia da depuração: comparar o que o programa faz com o que você acha que ele faz.
  • A depurar com print, e o atalho f"{variavel=}".
  • A pausar o programa no meio com breakpoint() e olhar as variáveis.
  • Os comandos do pdb: l, n, s, p, c, q, e também w, b, ll e h.
  • A rodar um programa inteiro dentro do depurador com python3 -m pdb, e a examinar o programa logo depois de um erro.
  • Os enganos comuns: variável que ainda não existe, variável com nome de comando, breakpoint() esquecido no código.

A ideia: assistir ao programa em câmera lenta

Pense numa receita de bolo que deu errado: o bolo saiu salgado. Ler a receita de novo não ajuda, porque você acha que seguiu tudo certinho. O que ajuda é refazer a receita olhando cada passo: "pus o açúcar… espera, esse pote é o do sal!". O defeito está no ponto em que o que aconteceu deixa de bater com o que você esperava.

Depurar é isso. Você escolhe um ponto do programa, olha o valor das variáveis ali, e compara com o que esperava. Se bate, o defeito está mais adiante; se não bate, está antes. Há duas lupas para olhar:

LupaComo funcionaParece com…
printVocê põe prints no código e roda de novo. Eles mostram os valores enquanto o programa passaDeixar bilhetes pelo caminho
pdb / breakpoint()O programa para num ponto, e você pergunta o que quiser, avança uma linha por vez, seguePausar o filme e andar quadro a quadro

1. O programa com defeito

A função soma o carrinho e cobra R$ 15 de frete, que fica de graça acima de R$ 100:

def total_com_frete(itens, frete):
    total = 0
    for preco, quantidade in itens:
        subtotal = preco * quantidade
        total = subtotal
    if total > 100:
        frete = 0
    return total + frete


carrinho = [(27.90, 2), (8.49, 3), (4.99, 1)]
print(f"Total: R$ {total_com_frete(carrinho, 15):.2f}")
$ python3 carrinho.py
Total: R$ 19.99

Faça a conta de cabeça: 2 arroz a 27,90 dão 55,80; 3 feijão a 8,49 dão 25,47; 1 açúcar, 4,99. Os produtos somam 86,26; com o frete, 101,26. O programa disse 19,99. Nenhum erro, nenhum traceback. Onde está o problema? (Se você já viu, ótimo; finja que não, e acompanhe o método, que vale para os defeitos que não se veem a olho.)


2. Depurando com print

A primeira lupa: um print dentro do laço, mostrando tudo o que muda a cada volta. Costuma-se começar a linha com algo como DEBUG:, para distinguir da saída normal (e lembrar de apagar depois):

def total_com_frete(itens, frete):
    total = 0
    for preco, quantidade in itens:
        subtotal = preco * quantidade
        total = subtotal
        print("DEBUG:", preco, quantidade, subtotal, total)
    if total > 100:
        frete = 0
    return total + frete


carrinho = [(27.90, 2), (8.49, 3), (4.99, 1)]
print(f"Total: R$ {total_com_frete(carrinho, 15):.2f}")
$ python3 carrinho_print.py
DEBUG: 27.9 2 55.8 55.8
DEBUG: 8.49 3 25.47 25.47
DEBUG: 4.99 1 4.99 4.99
Total: R$ 19.99

Leia as três linhas comparando com o esperado. Na primeira volta, total é 55,8: certo. Na segunda, devia ser 55,8 + 25,47 = 81,27, mas é 25,47, igual ao subtotal. Achou: o total não está acumulando, está sendo trocado a cada volta. No fim ele vale só o último item (4,99), e 4,99 + 15 = 19,99.

O atalho = das f-strings

Com quatro números soltos, é fácil esquecer qual é qual. Nas f-strings, um = depois do nome da variável mostra o nome e o valor:

        print(f"{subtotal=} {total=}")
$ python3 carrinho_igual.py
subtotal=55.8 total=55.8
subtotal=25.47 total=25.47
subtotal=4.99 total=4.99
Total: R$ 19.99

f"{total=}" vira total=55.8. Funciona com qualquer expressão: f"{preco * quantidade=}" mostra preco * quantidade=55.8.

Missão: conserte o carrinho. Se quiser, ponha antes um print(f"{total=}") dentro do laço para ver o defeito acontecer; depois corrija a linha do total para que ele acumule os subtotais (e apague o print de depuração). O total certo é R$ 101.26.

def total_com_frete(itens, frete):
    total = 0
    for preco, quantidade in itens:
        subtotal = preco * quantidade
        total = subtotal
    if total > 100:
        frete = 0
    return total + frete


carrinho = [(27.90, 2), (8.49, 3), (4.99, 1)]
print(f"Total: R$ {total_com_frete(carrinho, 15):.2f}")
def total_com_frete(itens, frete):
    total = 0
    for preco, quantidade in itens:
        subtotal = preco * quantidade
        total += subtotal
    if total > 100:
        frete = 0
    return total + frete


carrinho = [(27.90, 2), (8.49, 3), (4.99, 1)]
print(f"Total: R$ {total_com_frete(carrinho, 15):.2f}")
Quando o print é a melhor lupa

O print é rápido, funciona em qualquer lugar e mostra a história inteira de uma vez (todas as voltas do laço). O ponto fraco: a cada pergunta nova ("e o preco?"), você edita e roda de novo, e no fim precisa apagar todos os prints. Em programas que vão continuar rodando por muito tempo, o lugar dos bilhetes é o logging, que pode ser ligado e desligado sem apagar nada.


3. Pausando o programa: breakpoint()

A segunda lupa. A função embutida breakpoint() ("ponto de parada") faz o programa parar naquela linha e abrir o pdb, o depurador do Python (Python debugger). Ali você pode perguntar o valor de qualquer variável, executar uma linha por vez e, quando quiser, deixar o programa seguir.

def total_com_frete(itens, frete):
    total = 0
    for preco, quantidade in itens:
        breakpoint()
        subtotal = preco * quantidade
        total = subtotal
    if total > 100:
        frete = 0
    return total + frete


carrinho = [(27.90, 2), (8.49, 3), (4.99, 1)]
print(f"Total: R$ {total_com_frete(carrinho, 15):.2f}")

Abaixo, a sessão de verdade, no terminal da Ana. Depois de cada (Pdb), o que ela digitou; o resto é a resposta do Python. Ela vai olhar a primeira volta do laço, deixar o programa seguir até a segunda e conferir o total:

$ python3 carrinho_bp.py
> /home/ana/carrinho_bp.py(4)total_com_frete()
-> breakpoint()
(Pdb) l
  1  	def total_com_frete(itens, frete):
  2  	    total = 0
  3  	    for preco, quantidade in itens:
  4  ->	        breakpoint()
  5  	        subtotal = preco * quantidade
  6  	        total = subtotal
  7  	    if total > 100:
  8  	        frete = 0
  9  	    return total + frete
 10
 11
(Pdb) p preco, quantidade
(27.9, 2)
(Pdb) n
> /home/ana/carrinho_bp.py(5)total_com_frete()
-> subtotal = preco * quantidade
(Pdb) n
> /home/ana/carrinho_bp.py(6)total_com_frete()
-> total = subtotal
(Pdb) p subtotal, total
(55.8, 0)
(Pdb) n
> /home/ana/carrinho_bp.py(3)total_com_frete()
-> for preco, quantidade in itens:
(Pdb) p total
55.8
(Pdb) c
> /home/ana/carrinho_bp.py(4)total_com_frete()
-> breakpoint()
(Pdb) p preco, quantidade, total
(8.49, 3, 55.8)
(Pdb) n
> /home/ana/carrinho_bp.py(5)total_com_frete()
-> subtotal = preco * quantidade
(Pdb) n
> /home/ana/carrinho_bp.py(6)total_com_frete()
-> total = subtotal
(Pdb) n
> /home/ana/carrinho_bp.py(3)total_com_frete()
-> for preco, quantidade in itens:
(Pdb) p total
25.47
(Pdb) q
Quitting pdb will kill the process. Quit anyway? [y/n] y

Vamos por partes:

> /home/ana/carrinho_bp.py(4)total_com_frete()
Onde o programa está parado: arquivo, linha 4, dentro da função total_com_frete.
-> breakpoint()
A próxima linha a ser executada (ela ainda não rodou).
(Pdb)
O "prompt" do depurador: ele espera um comando seu, como o >>> do modo interativo.
l
list, "listar": mostra o código em volta, com uma setinha -> na linha atual.
p preco, quantidade
print, "mostrar": calcula e mostra o valor. Primeira volta: 27,9 e 2.
n
next, "próxima": executa a linha atual e para na seguinte. Dois n e a Ana está na linha 6, com o subtotal já calculado (55,8) e o total ainda 0.
c
continue, "continuar": deixa o programa correr até a próxima parada. Como o breakpoint() está dentro do laço, ele para de novo na segunda volta.
p total → 25.47
O momento "achei!": no começo da segunda volta, o total era 55,8; depois da linha 6, virou 25,47 em vez de 81,27. A linha 6 troca o valor em vez de somar.
q
quit, "sair": encerra o depurador e o programa. No Python 3.14, quando o pdb foi aberto por um breakpoint(), ele pede confirmação: Quit anyway? [y/n] ("Sair do pdb vai matar o processo. Sair mesmo assim?"). A Ana respondeu y (yes, "sim"; n é no, "não") 3.14.
Variáveis, não comandos

No (Pdb), você pode digitar o nome de uma variável sozinho (total) e ele mostra o valor, ou até uma conta (preco * quantidade). O p é necessário quando o nome da variável é igual ao de um comando; veja em Quando dá errado. E você pode mudar uma variável no meio (total = 100) para ver o que aconteceria.

Os comandos principais

ComandoVem deO que faz
llistMostra o código perto da linha atual (de novo: continua a listagem)
lllonglistMostra a função inteira em que você está
nnextExecuta a linha atual e para na próxima, sem entrar nas funções chamadas
sstepExecuta a linha atual e, se ela chama uma função sua, entra nela
ccontinueSegue até a próxima parada (ou até o fim)
p expressãoprintMostra o valor da expressão
pp expressãopretty printMostra arrumado (bom para listas e dicionários grandes)
wwhereMostra o caminho de chamadas até aqui (como um traceback)
b 6breakMarca uma parada na linha 6 (b sozinho lista as paradas)
cl 1clearApaga a parada de número 1
qquitSai do depurador e encerra o programa
h / h nhelpLista os comandos / explica um comando
(Enter vazio)Repete o último comando (útil para apertar n várias vezes)

Esqueceu o que um comando faz? Pergunte ao próprio pdb:

(Pdb) h n
      Usage: n(ext)

      Continue execution until the next line in the current function
      is reached or it returns.
(Pdb) h p
      Usage: p expression

      Print the value of the expression.

"Uso: n(ext). Continua a execução até chegar à próxima linha da função atual, ou até ela retornar." O n(ext) quer dizer que você pode digitar n ou next.

No terminal do 3.14, com cores

No Python 3.14, o código que o pdb mostra (no l, no ll, na linha ->) aparece colorido, como no modo interativo 3.14. As sessões desta página estão sem cor para ficarem fáceis de ler.


4. O programa inteiro no depurador: python3 -m pdb

Você não precisa editar o programa para pôr um breakpoint(). Com python3 -m pdb programa.py, o depurador começa parado na primeira linha. Aqui a Ana usa o s para entrar na função, o w para ver o caminho, o b para marcar uma parada mais adiante e o ll para ver a função inteira:

$ python3 -m pdb carrinho.py
> /home/ana/carrinho.py(1)<module>()
-> def total_com_frete(itens, frete):
(Pdb) n
> /home/ana/carrinho.py(11)<module>()
-> carrinho = [(27.90, 2), (8.49, 3), (4.99, 1)]
(Pdb) n
> /home/ana/carrinho.py(12)<module>()
-> print(f"Total: R$ {total_com_frete(carrinho, 15):.2f}")
(Pdb) s
--Call--
> /home/ana/carrinho.py(1)total_com_frete()
-> def total_com_frete(itens, frete):
(Pdb) n
> /home/ana/carrinho.py(2)total_com_frete()
-> total = 0
(Pdb) n
> /home/ana/carrinho.py(3)total_com_frete()
-> for preco, quantidade in itens:
(Pdb) n
> /home/ana/carrinho.py(4)total_com_frete()
-> subtotal = preco * quantidade
(Pdb) w
  /usr/lib/python3.14/bdb.py(913)run()
-> exec(cmd, globals, locals)
  <string>(1)<module>()
  /home/ana/carrinho.py(12)<module>()
-> print(f"Total: R$ {total_com_frete(carrinho, 15):.2f}")
> /home/ana/carrinho.py(4)total_com_frete()
-> subtotal = preco * quantidade
(Pdb) b 6
Breakpoint 1 at /home/ana/carrinho.py:6
(Pdb) c
> /home/ana/carrinho.py(6)total_com_frete()
-> if total > 100:
(Pdb) p total
4.99
(Pdb) ll
  1  	def total_com_frete(itens, frete):
  2  	    total = 0
  3  	    for preco, quantidade in itens:
  4  	        subtotal = preco * quantidade
  5  	        total = subtotal
  6 B->	    if total > 100:
  7  	        frete = 0
  8  	    return total + frete
(Pdb) q
  1. Começo: parado na linha 1, no <module> (o "corpo" do arquivo, fora de qualquer função). O primeiro n pulou da linha 1 para a 11: o def só cria a função, sem rodar o que está dentro dela.
  2. s na linha 12: em vez de executar a linha inteira (como o n faria), o s entrou na função total_com_frete. O --Call-- avisa "entrei numa chamada".
  3. w: o caminho até aqui, de cima para baixo. As duas primeiras linhas são do próprio pdb (pode ignorar); depois, a linha 12 do programa, que chamou a função; por último, com o >, onde você está agora.
  4. b 6 e c: uma parada na linha 6 (logo depois do laço) e "siga até lá". O laço rodou inteiro sem parar, e na linha 6 o total é 4,99: o valor do último item só.
  5. ll: a função inteira. O B marca a parada; o ->, onde você está.
  6. q: sai. Aberto com python3 -m pdb, o pdb não pede confirmação.
n (next)s (step)
Numa linha que chama uma função suaExecuta a função inteira e para na linha seguinteEntra na função e para na primeira linha dela
Numa linha comumOs dois fazem o mesmo: executam a linha e param na próxima
Quando usarVocê confia na função e quer passar por cimaVocê desconfia da função e quer ver por dentro

5. Depois que o programa quebrou: o post mortem

A média da turma dá erro:

def media(notas):
    return sum(notas) / len(notas)


turma = {"Ana": [8, 9, 7], "Bia": [], "Caio": [6, 5, 8]}
for aluno, notas in turma.items():
    print(aluno, media(notas))
$ python3 media.py
Ana 8.0
Traceback (most recent call last):
  File "/home/ana/media.py", line 7, in <module>
    print(aluno, media(notas))
                 ~~~~~^^^^^^^
  File "/home/ana/media.py", line 2, in media
    return sum(notas) / len(notas)
           ~~~~~~~~~~~^~~~~~~~~~~~
ZeroDivisionError: division by zero

"Divisão por zero." O traceback diz onde, mas não com quais valores. Rodando com python3 -m pdb e mandando seguir (c), o pdb para no momento do erro, com todas as variáveis daquele instante. Isso se chama depuração post mortem ("depois da morte"):

$ python3 -m pdb media.py
> /home/ana/media.py(1)<module>()
-> def media(notas):
(Pdb) c
Ana 8.0
Traceback (most recent call last):
  File "/usr/lib/python3.14/pdb.py", line 3652, in main
    pdb._run(target)
    ~~~~~~~~^^^^^^^^
  File "/usr/lib/python3.14/pdb.py", line 2566, in _run
    self.run(target.code)
    ~~~~~~~~^^^^^^^^^^^^^
  File "/usr/lib/python3.14/bdb.py", line 913, in run
    exec(cmd, globals, locals)
    ~~~~^^^^^^^^^^^^^^^^^^^^^^
  File "<string>", line 1, in <module>
  File "/home/ana/media.py", line 7, in <module>
    print(aluno, media(notas))
                 ~~~~~^^^^^^^
  File "/home/ana/media.py", line 2, in media
    return sum(notas) / len(notas)
           ~~~~~~~~~~~^~~~~~~~~~~~
ZeroDivisionError: division by zero
Uncaught exception. Entering post mortem debugging
Running 'cont' or 'step' will restart the program
> /home/ana/media.py(2)media()
-> return sum(notas) / len(notas)
(Pdb) p aluno, notas
('Bia', [])
(Pdb) q

O traceback ficou mais comprido (as primeiras linhas são do próprio pdb; o que interessa começa em media.py). Depois vem: "Exceção não tratada. Entrando na depuração post mortem. Rodar cont ou step vai recomeçar o programa." E o (Pdb) está parado exatamente na linha que quebrou. Um p mostra o culpado: a Bia ainda não tem notas, a lista está vazia, e len([]) é 0.

Missão: conserte a média da turma para não quebrar com a lista vazia da Bia: quando o aluno não tiver notas, mostre Bia: sem notas; para os outros, a média com uma casa decimal, como já está.

def media(notas):
    return sum(notas) / len(notas)


turma = {"Ana": [8, 9, 7], "Bia": [], "Caio": [6, 5, 8]}
for aluno, notas in turma.items():
    print(f"{aluno}: {media(notas):.1f}")
def media(notas):
    return sum(notas) / len(notas)


turma = {"Ana": [8, 9, 7], "Bia": [], "Caio": [6, 5, 8]}
for aluno, notas in turma.items():
    if notas:
        print(f"{aluno}: {media(notas):.1f}")
    else:
        print(f"{aluno}: sem notas")

Formas diferentes de depurar

FormaComoQuando usar
print(...)Bilhetes no código; roda de novoVer a história inteira (todas as voltas de um laço); defeitos simples
print(f"{x=}")O mesmo, com o nome juntoSempre que for usar print para depurar
breakpoint()Pausa naquela linha e abre o pdbVocê sabe onde olhar e quer perguntar várias coisas sem rodar de novo
python3 -m pdb programa.pyComeça parado na primeira linhaSeguir o programa desde o começo, sem editar o arquivo
python3 -m pdb + cPara no momento do erro (post mortem)O programa quebra e você quer os valores daquele instante
loggingMensagens com nível (DEBUG, INFO…), ligadas ou desligadasProgramas que ficam rodando; você quer as pistas guardadas
Depurador de um editor (VS Code, PyCharm)O mesmo que o pdb, com botões e painéisQuando você já usa um desses editores
Onde praticar o pdb

As sessões desta aula são de um terminal de verdade, onde você digita cada comando e vê a resposta. É lá que vale treinar o pdb: crie o carrinho_bp.py no seu computador e refaça a sessão da seção 3. O breakpoint() também funciona nas caixas "Experimente": o programa para no (Pdb) e você digita p total, n, c… no terminal da caixa (lá, sem as cores). As missões desta página usam a outra lupa, o print.


Quando dá errado

A variável que (ainda) não existe
$ python3 carrinho_bp.py
> /home/ana/carrinho_bp.py(4)total_com_frete()
-> breakpoint()
(Pdb) p subtotl
*** NameError: name 'subtotl' is not defined
(Pdb) subtotal
*** NameError: name 'subtotal' is not defined
(Pdb) total
0

As linhas com *** são o jeito do pdb mostrar um erro sem derrubar a sessão: você continua no (Pdb). A primeira é um erro de digitação (subtotl). A segunda é mais curiosa: subtotal está escrito certo, mas o programa está parado na linha 4, e o subtotal só é criado na linha 5, que ainda não rodou. Lembre: a linha da setinha -> é a próxima, não a última. Como consertar: confira o nome, ou avance com n até a variável ser criada.

Uma variável com nome de comando
notas = [8, 6, 9]
soma = 0
for n in notas:
    breakpoint()
    soma += n
print(soma)
$ python3 soma_notas.py
> /home/ana/soma_notas.py(4)<module>()
-> breakpoint()
(Pdb) n
> /home/ana/soma_notas.py(5)<module>()
-> soma += n
(Pdb) p n
8
(Pdb) q
Quitting pdb will kill the process. Quit anyway? [y/n] y

A Ana queria ver a variável n, mas n sozinho é o comando next: o programa andou uma linha. Quando o nome da variável é igual a um comando (n, s, c, l, p, q, b, w…), use p na frente: p n. Mais um motivo para dar nomes claros às variáveis: for nota in notas:.

O breakpoint() esquecido no código

A Ana mandou o programa para uma colega sem tirar o breakpoint(). Para quem roda, o programa simplesmente "trava" num (Pdb) que ela não pediu. E se o programa roda sem ninguém no teclado (com a entrada vindo de um arquivo, por exemplo), o pdb lê o fim da entrada como "sair":

$ python3 carrinho_bp.py < /dev/null
> /home/ana/carrinho_bp.py(4)total_com_frete()
-> breakpoint()
(Pdb)
Quitting pdb will kill the process. Quit anyway? [y/n]
$ echo $?
1

O programa terminou no meio, sem mostrar o total, com o código de saída 1 (falha). Como consertar: apague os breakpoint() antes de entregar o programa (procure por eles: grep -n breakpoint *.py). E, para rodar sem as paradas sem editar nada, existe a variável de ambiente PYTHONBREAKPOINT=0, que desliga todos os breakpoint():

$ PYTHONBREAKPOINT=0 python3 carrinho_bp.py
Total: R$ 19.99
Consertar o sintoma em vez da causa

Não é uma mensagem de erro, mas é o engano mais comum de quem depura. Com o carrinho dando 19,99, dá vontade de "ajustar" o resultado (somar a diferença no fim, mudar o frete) até sair o número certo para aquele carrinho. O próximo carrinho vai dar errado de novo. Depurar é achar a linha em que o valor deixa de ser o esperado (a linha 6, total = subtotal) e consertar ali.


Referência rápida

EscritaO que faz
print(f"{variavel=}")Mostra variavel=valor
breakpoint()Para o programa ali e abre o pdb
python3 -m pdb programa.pyRoda o programa no depurador, parado na primeira linha; para também se der erro
PYTHONBREAKPOINT=0 python3 programa.pyRoda ignorando os breakpoint()
import pdb; pdb.set_trace()O jeito antigo de escrever breakpoint() (você vai ver em códigos mais velhos)
l / llCódigo perto da linha atual / a função inteira
n / sPróxima linha, passando por cima / entrando nas funções
cContinua até a próxima parada
p x / pp xMostra o valor / mostra arrumado
wOnde estou (caminho de chamadas)
b 12 / cl 1Parada na linha 12 / apaga a parada 1
qSai (no 3.14, depois de um breakpoint(), pede confirmação: y)
h / h comandoAjuda
Novidades do pdb

No Python 3.14, além das cores e da confirmação no q, o pdb ganhou a opção -p PID, que conecta o depurador a um programa Python que já está rodando (python3 -m pdb -p 1234, onde 1234 é o número do processo) 3.14. No Python 3.15, o (Pdb) passou a usar o mesmo editor de linha do modo interativo novo. Veja as Novidades do Python 3.14 e 3.15.


Exercícios

1. O atalho da f-string

Complete o print para que ele mostre preco=27.9:

print(f"{preco}")

2. A função que pausa

Qual função embutida, escrita numa linha do programa, faz ele parar ali e abrir o depurador?

3. O pdb está parado numa linha que chama calcular_frete(total), e você quer ver o que acontece dentro dessa função. Qual comando?

4. O pdb mostra -> desconto = preco * 0.1. Nesse momento, a variável desconto…

5. No (Pdb), como ver o valor de uma variável chamada c?

6. Um programa quebra com ZeroDivisionError e você quer ver o valor das variáveis no momento do erro. O que fazer?

Missão final: as vogais da Ana

Missão: a função devia contar as vogais de "Ana Beatriz" (A, a, e, a, i: são 5), mas diz 4. Ponha um print de depuração dentro do for (por exemplo, print(f"{letra=} {total=}")), descubra qual letra não está sendo contada e conserte a função. Depois, apague o print de depuração.

def contar_vogais(texto):
    total = 0
    for letra in texto:
        if letra in "aeiou":
            total += 1
    return total


print("Vogais:", contar_vogais("Ana Beatriz"))
def contar_vogais(texto):
    total = 0
    for letra in texto:
        if letra.lower() in "aeiou":
            total += 1
    return total


print("Vogais:", contar_vogais("Ana Beatriz"))

O print mostra a letra 'A' passando sem o total mudar: "A" maiúsculo não está em "aeiou". Há dois consertos: letra.lower() in "aeiou" ou letra in "aeiouAEIOU". E se o texto fosse "Érica"? Pense no que o print mostraria.


Resumo

  • Depurar é comparar, ponto a ponto, o que o programa faz com o que você espera, até achar a linha em que os dois se separam.
  • print mostra a história inteira; print(f"{x=}") põe o nome junto. Apague os prints de depuração no fim.
  • breakpoint() pausa o programa e abre o pdb. A linha da setinha -> é a próxima a rodar.
  • Comandos: l lista, n próxima linha, s entra na função, p mostra um valor, c continua, q sai (no 3.14, confirme com y); e w, b, ll, h.
  • python3 -m pdb programa.py roda tudo no depurador; se o programa quebrar, ele para no erro (post mortem).
  • Variável com nome de comando? Use p. Não esqueça breakpoint() no código; PYTHONBREAKPOINT=0 desliga todos.