- Aprender
- Computação e TI
- Programação
- Introdução à programação com Python
- Módulos e depuração em Python
Módulos e depuração em Python: testes unitários
math e random, escreva seus próprios módulos e instale pacotes do PyPI com pip. Depois, leia rastreamentos de exceção e mensagens de erro, encontre defeitos com um processo de depuração passo a passo e verifique suas funções com instruções assert e seus primeiros testes unitários.BásicoAula 9 de 10 do curso Curso de Introdução à programação com Python
O que você vai aprender
- Inclua código pronto em um programa com instruções import, use módulos da biblioteca padrão, como
matherandom, divida seu próprio código em módulos e instale pacotes de terceiros com pip.- Inclua código em um programa com instruções importUse
import,from … importeascorretamente, acesse nomes com a notação com ponto e distinga os erros de importação. - Calcule e faça simulações com math, random e outros módulos padrãoUse as principais funções de
matherandom, evite seus erros comuns e conheça alguns outros módulos da biblioteca padrão de uso cotidiano. - Escreva e importe seus próprios módulosDivida o código em seus próprios arquivos
.py, importe-os, evite conflitos de nomes e useif __name__ == "__main__":para que um arquivo possa ser executado e importado. - Instale e gerencie pacotes com pipInstale, inspecione, atualize e remova pacotes de terceiros do PyPI, importe-os com o nome correto e resolva os problemas comuns de instalação.
- Inclua código em um programa com instruções import
- Use a saída de erro do Python para encontrar onde e por que um programa falhou e distinga erros de sintaxe, erros em tempo de execução e erros de lógica.
- Leia um traceback de baixo para cimaEncontre o tipo de exceção, a mensagem, a linha que falhou e a cadeia de chamadas que levou até ela.
- Reconheça erros comuns e suas causasRelacione os tipos de exceção mais frequentes às suas causas e correções comuns e identifique erros de lógica que não produzem mensagem.
- Leia um traceback de baixo para cima
- Localize a causa de um defeito com um processo passo a passo, em vez de adivinhar, e inspecione um programa em execução com instruções print e um depurador.
- Siga um processo de depuração passo a passoReproduza um defeito, formule e teste uma hipótese, delimite a causa, corrija-a e teste novamente.
- Inspecione um programa com instruções print, tabelas de rastreamento e um depuradorObserve como os valores mudam durante a execução do código, adicionando impressões, rastreando à mão ou pausando em pontos de interrupção.
- Siga um processo de depuração passo a passo
- Use
assertpara declarar o que deve ser verdadeiro, escreva pequenos testes automatizados para suas funções e execute e leia esses testes com o módulo padrãounittest.- Verifique suposições com instruções assertEscreva instruções
assertcom mensagens claras e saiba quando as asserções são a ferramenta adequada e quando não são. - Escreva seus primeiros testes unitários para funçõesEscreva funções de teste com asserções, escolha casos típicos, de limite e extremos e use testes que falham para encontrar e corrigir defeitos.
- Organize e execute testes com unittestEscreva testes com
unittest.TestCase, execute-os e leia o relatório de testes aprovados, falhas e erros.
- Verifique suposições com instruções assert
Guia de estudo de Módulos e depuração em Python
1Reutilize módulos e instale pacotes
O código importado permite que um programa pequeno use cálculos, simulações e ferramentas escritos em outro lugar. Você escolherá uma forma de importação, trabalhará com funções da biblioteca padrão e organizará suas próprias funções para que outro arquivo possa reutilizá-las. A instalação acrescenta outra questão: qual Python recebe um pacote de terceiros? Entender essa relação facilita o diagnóstico de um módulo ausente e mantém claros os nomes de cada programa.
1.1 Importe um módulo ou nomes selecionados
Um módulo contém código Python que outro programa pode reutilizar. A maioria dos módulos são arquivos .py. A biblioteca padrão acompanha o Python; código de terceiros precisa de uma instalação separada. Um pacote agrupa módulos relacionados, normalmente em uma pasta.
Relacione a chamada à importação
Uma importação cria nomes no seu programa. A forma escolhida determina como você chama o código importado.
| Instrução | Nome criado | Exemplo de chamada |
|---|---|---|
import math | math | math.sqrt(16) |
from math import sqrt | sqrt | sqrt(16) |
import statistics as stats | stats | stats.mean([2, 4, 9]) |
from random import randint as roll | roll | roll(1, 6) |
Notação com ponto significa consultar um nome dentro de um módulo ou objeto. Em math.sqrt(16), o Python encontra sqrt dentro de math e o chama com 16. O resultado é 4.0. A constante math.pi não precisa de parênteses de chamada porque é um valor, não uma função.
Após import math, apenas sqrt(16) lança NameError: o programa tem um nome para o módulo, mas nenhum nome separado para sqrt. Após apenas from math import sqrt, o erro inverso, math.sqrt(16), também lança NameError porque math não foi associado. Um nome alternativo substitui o nome usual da associação importada: import statistics as stats cria stats, não ambos os nomes.
Faça o mesmo cálculo de duas formas
Um triângulo retângulo tem lados 3 e 4. Primeiro eleve os lados ao quadrado e some-os; depois extraia a raiz quadrada.
import math
length = math.sqrt(3**2 + 4**2)
print(length) # 5.0
A expressão dentro da chamada torna-se 9 + 16, depois 25; sqrt retorna 5.0. Com from math import sqrt, escreva length = sqrt(3**2 + 4**2). Usar o prefixo do módulo torna visível a origem de uma função e mantém os outros nomes do módulo fora do espaço de nomes do programa. A própria associação de math ainda pode substituir uma variável anterior chamada math.
Mantenha os nomes claros
Evite from math import *. Essa importação com curinga traz nomes públicos para seu programa sem nomeá-los individualmente. Por exemplo, e = 5 seguido dessa importação altera e para 2.718281828459045. Assim, uma importação que parece não ter relação pode alterar um cálculo posterior. A PEP 8 recomenda importações explícitas e as coloca no início, com um módulo por linha: primeiro a biblioteca padrão, depois pacotes de terceiros e, por último, seus próprios módulos.
O Python executa o código de nível superior de um módulo em sua primeira importação durante a execução de um programa. Importações posteriores reutilizam o módulo carregado; elas não o iniciam novamente.
Use o erro para escolher a correção
| Erro | O que inspecionar |
|---|---|
ModuleNotFoundError de import mathh | A grafia e a instalação do módulo |
ImportError de from math import square_root | O nome solicitado do módulo |
AttributeError de math.sqroot(16) | O nome após o ponto |
No interpretador interativo, execute import math e depois dir(math) para listar seus nomes. Execute help(math.sqrt) para ler a documentação de uma função. A referência oficial da biblioteca Python explica seus argumentos e resultado.
Principais conclusões
import modulecria uma associação para o módulo; acesse seu conteúdo com a notação com ponto.from module import namecria a associação selecionada;asescolhe um nome alternativo.- Importações explícitas deixam clara a origem dos nomes e reduzem substituições acidentais.
- Erros de importação distinguem um módulo não encontrado, um nome importado indisponível e um atributo ausente.
dir()lista nomes;help()explica como usar um deles.
1.2 Escolha funções de math e random
A biblioteca padrão oferece ferramentas testadas para cálculos e simulações. A escolha de uma função começa pelo resultado de que você precisa: uma contagem inteira, uma medida arredondada, um item aleatório ou uma lista reordenada.
Arredondar tem vários significados
Após import math, math.sqrt(16) retorna o número de ponto flutuante 4.0. math.pi e math.e fornecem as constantes 3.141592653589793 e 2.718281828459045. math.factorial(5) multiplica os números inteiros de 1 a 5 e retorna 120.
| Expressão | Resultado | Significado |
|---|---|---|
math.floor(7.9) | 7 | Maior inteiro que não supera o valor |
math.ceil(2.1) | 3 | Menor inteiro que não é inferior ao valor |
math.floor(-2.5) | -3 | Para baixo significa em direção ao infinito negativo |
int(-3.5) | -3 | A conversão corta em direção a zero |
round(2.5) | 2 | Um valor exatamente na metade é arredondado para o par |
round(3.5) | 4 | O inteiro par mais próximo é 4 |
Para 23 metros quadrados de parede e 10 metros quadrados por lata de tinta, a divisão resulta em 2.3 latas. Comprar latas inteiras exige math.ceil(23 / 10), que retorna 3. round(2.3) resulta em 2 e deixa tinta insuficiente.
A função embutida round() não precisa ser importada. Para um círculo de raio 2, round(math.pi * 2 ** 2, 2) resulta em 12.57. O arredondamento altera a precisão de um número; sua função é diferente da de comprar recipientes inteiros suficientes.
Verifique domínios e comparações de números de ponto flutuante
math.sqrt(-1) lança ValueError porque não há raiz quadrada real para essa entrada. Números de ponto flutuante calculados também exigem cuidado: o armazenamento em binário faz com que 0.1 + 0.2 == 0.3 seja False. math.isclose(0.1 + 0.2, 0.3) é True; use-o para comparar aproximadamente números de ponto flutuante calculados.
Selecione a operação aleatória
O módulo random usa um algoritmo para produzir resultados pseudoaleatórios.
| Chamada | Resultado |
|---|---|
random.randint(1, 6) | Um inteiro de 1 a 6, incluindo ambos |
random.randrange(1, 6) | Um inteiro de 1 a 5 |
random.random() | Um número de ponto flutuante maior ou igual a 0,0 e menor que 1,0 |
random.uniform(a, b) | Um número de ponto flutuante entre os extremos fornecidos |
random.choice(items) | Um item |
random.sample(items, k) | Itens de k posições, sem reposição |
random.shuffle(items) | Reordena a lista existente; retorna None |
Mantenha random.shuffle(cards) em uma linha própria. Atribuir seu resultado de volta a cards faz com que o nome deixe de estar associado à lista e passe a estar associado a None. Amostrar posições também é diferente de exigir valores distintos: random.sample([1, 1], 2) retorna [1, 1].
Execute uma simulação reproduzível
import random
sevens = 0
for _ in range(1000):
if random.randint(1, 6) + random.randint(1, 6) == 7:
sevens += 1
print(sevens)
Seis dos 36 pares igualmente prováveis somam 7, então a contagem geralmente fica perto de 167, mas muda entre execuções. Para reproduzir uma sequência durante a depuração, chame random.seed(42) antes de gerar os valores, usando a mesma versão do Python e sequência de chamadas. Para senhas ou tokens de segurança, use secrets.
Encontre outras ferramentas de uso cotidiano
statistics.mean([2, 4, 9]) retorna 5; statistics.median([2, 4, 9]) retorna 4. datetime.date.today() fornece a data de hoje. Após import sys, sys.argv contém o nome do script seguido de seus argumentos: executar python greet.py Ada fornece ['greet.py', 'Ada'].
Principais conclusões
floor,ceil,interoundrespondem a diferentes necessidades de arredondamento.- Use
math.iscloseao comparar números de ponto flutuante calculados. randintinclui ambos os extremos;randrangeexclui seu limite de parada.sampleseleciona posições sem reposição, então valores duplicados na entrada podem se repetir.shufflealtera a própria lista e retornaNone.- Uma semente fixa ajuda a reproduzir o comportamento aleatório; tokens de segurança exigem
secrets.
1.3 Torne um arquivo reutilizável sem iniciar seu programa
Uma função fica mais fácil de reutilizar quando está em um módulo com uma finalidade bem definida. Outro programa pode importar essa função, e um teste pode verificá-la sem pedir que uma pessoa execute toda a aplicação.
Separe a função do código chamador
Salve este código como geometry.py:
"""Calculations for simple shapes."""
import math
def circle_area(radius):
"""Return the area of a circle with the given radius."""
return math.pi * radius ** 2
Na mesma pasta, salve este código chamador como main.py:
import geometry
print(geometry.circle_area(2))
Execute python main.py. O Python encontra geometry, cria sua função e, em seguida, o código chamador executa essa função. O resultado exibido é 12.566370614359172. Importe sem a extensão .py. Um nome de módulo válido usa letras, dígitos e sublinhados, com um primeiro caractere válido; my_tools.py funciona com import my_tools, enquanto import my-tools é sintaxe inválida.
As strings no início do módulo e da função são docstrings. Elas descrevem a finalidade do código e aparecem em help(geometry) e help(geometry.circle_area).
Entenda o que a importação executa
Na primeira importação, o Python executa as instruções de nível superior do módulo em ordem. Uma instrução def cria uma função; ela não executa o corpo dessa função. Porém, um print(circle_area(2)) sem proteção no final de geometry.py exibiria o resultado durante a importação.
Esse efeito colateral é relevante quando um módulo contém solicitações de entrada ou um laço do programa. Um teste que só precisa de circle_area não deveria ter que iniciar o programa e responder às suas solicitações.
Proteja a inicialização e mantenha a função disponível
Adicione isto abaixo da função reutilizável em geometry.py:
def main():
print(circle_area(2))
if __name__ == "__main__":
main()
O Python define __name__ para cada módulo. Uma execução direta, python geometry.py, atribui a ele "__main__", tornando a condição verdadeira e chamando main(). Importar geometry atribui a ele "geometry", então o Python pula a chamada. As definições acima da condição de execução principal continuam disponíveis nos dois casos.
Não coloque a própria circle_area dentro da condição. Quem importa precisa dessa definição mesmo quando a condição de inicialização é falsa. A condição controla quando a aplicação começa; ela não impede o Python de carregar o módulo.
Saiba onde o Python procura
O Python verifica módulos embutidos, como sys, e depois as pastas em sys.path, em ordem. Normalmente, a pasta do script em execução vem primeiro nesse caminho, seguida das pastas da biblioteca padrão e dos pacotes instalados. Manter seus próprios arquivos ao lado do script em execução é uma organização simples para um projeto de iniciante. Um módulo fora do caminho de busca não pode ser encontrado por uma importação comum.
Essa ordem também pode ocultar um módulo da biblioteca. Se você salvar seu programa como random.py, seu import random pode carregar seu arquivo em vez da biblioteca. Um random.randint(1, 6) posterior lança então AttributeError. Renomeie o arquivo, por exemplo, para dice.py; o Python 3.13 e versões posteriores podem incluir uma dica para renomeá-lo no erro.
Um módulo carregado é reutilizado na mesma execução. Em um interpretador interativo, editar o arquivo e importá-lo novamente mantém a versão carregada. Reinicie o interpretador antes de testar suas alterações.
Principais conclusões
- Importe seu próprio arquivo
.pyusando o nome do módulo sem a extensão. - Uma importação executa instruções de nível superior, enquanto os corpos das funções aguardam chamadas.
- Mantenha definições reutilizáveis fora da condição de execução principal
__main__e a inicialização do programa dentro dela. - Um arquivo local pode ocultar um módulo da biblioteca com o mesmo nome.
- Reinicie um interpretador interativo para testar novamente um módulo editado.
1.4 Instale pacotes para o Python que você executa
Pacotes de terceiros acrescentam código que não acompanha o Python. O PyPI, o Índice de Pacotes Python em https://pypi.org, hospeda esses projetos. O pip os baixa e instala para uma instalação específica do Python.
Use o terminal e depois use o Python
Um comando de terminal e uma instrução Python pertencem a lugares diferentes. O prompt >>> indica que você está no interpretador interativo do Python. Digitar pip install requests ali produz SyntaxError; o Python não consegue interpretá-lo como uma instrução.
No terminal, execute:
python -m pip install cowsay
Depois, coloque isto em um programa Python:
import cowsay
cowsay.cow("Hello, Ada")
O programa exibe uma vaca feita de texto ao lado de um balão de fala que contém a mensagem. A instalação disponibiliza o pacote; a importação cria a associação do programa para ele.
Associe o pip ao seu interpretador
A forma -m pip pede ao Python indicado que execute seu próprio pip. Use o comando que inicia seus programas: geralmente python3 no macOS ou Linux, ou py no Windows.
python3 -m pip install cowsay
python3 game.py
As duas linhas selecionam o mesmo comando Python. Se uma instalação informar que foi bem-sucedida, mas um script ainda lançar ModuleNotFoundError, compare o Python usado para instalar com o usado para executar o script. Um editor pode ter selecionado um interpretador diferente do seu terminal. Use o pip do interpretador selecionado antes de alterar a importação.
Inspecione e mantenha uma instalação
| Comando de terminal | Função |
|---|---|
python -m pip list | Listar pacotes instalados e versões |
python -m pip show requests | Mostrar detalhes, incluindo o local de instalação |
python -m pip install --upgrade requests | Atualizar para a versão mais recente disponível |
python -m pip install "requests==2.32.3" | Selecionar essa versão específica |
python -m pip uninstall requests | Remover o pacote |
Uma versão específica é útil quando você precisa do comportamento daquela versão. show ajuda a verificar qual local o pip realmente alterou.
Verifique o nome na documentação
O nome da distribuição digitado para o pip pode ser diferente do nome de importação. Instale beautifulsoup4 e depois use import bs4; instale Pillow e depois use import PIL. Instalar repetidamente um nome de importação não é uma correção útil quando a documentação indica uma distribuição com outro nome.
A biblioteca padrão do Python, incluindo math, random e unittest, já acompanha o Python. Esses módulos não exigem instalação com pip.
Respeite uma instalação gerenciada
Alguns sistemas operacionais e gerenciadores de pacotes protegem seu Python com um erro externally-managed-environment. Isso significa que uma instalação direta é bloqueada para manter a instalação gerenciada consistente. Use um ambiente virtual, uma pasta separada com Python e pacotes para o projeto. Não force a instalação com sudo pip install.
Qualquer pessoa pode publicar no PyPI. Antes de instalar, verifique a grafia exata e leia a página e a documentação do projeto. Nomes parecidos podem ser tentativas de registro de nomes semelhantes com erros de digitação. Prefira pacotes estabelecidos e mantidos e instale aqueles de que seu programa realmente precisa.
Principais conclusões
- Execute comandos pip em um terminal e importações em código Python.
- Use
python -m pipcom o mesmo comando de interpretador que executa o programa. - Verifique o nome de importação na documentação; ele pode ser diferente do nome de instalação.
- Use pip para inspecionar, atualizar, selecionar uma versão ou remover um pacote.
- Um Python gerenciado pode exigir um ambiente virtual para o projeto.
- Verifique a grafia e a documentação do pacote antes de instalar código de terceiros.
2Leia erros e tracebacks
A saída de erro do Python registra tanto a operação que falhou quanto as chamadas que levaram até ela. Ler essas pistas em ordem ajuda você a encontrar um ponto de partida útil, em vez de alterar a primeira linha que reconhecer. Você acompanhará um traceback até sua entrada, distinguirá tipos comuns de exceção e reconhecerá o caso menos evidente em que um programa é executado normalmente, mas produz um resultado incorreto. O comportamento esperado oferece algo concreto para verificar.
2.1 Acompanhe um traceback até a entrada incorreta
Um traceback informa quais chamadas estavam ativas quando uma exceção não tratada interrompeu o programa. Ele fornece tanto um ponto para começar a leitura quanto um caminho de volta ao valor que causou a falha.
Comece pela última linha
Considere este programa completo em scores.py:
def average(values):
return sum(values) / len(values)
def report(scores):
avg = average(scores)
print(f"Average: {avg:.1f}")
report([])
A última linha de seu relatório de erro é ZeroDivisionError: division by zero. Ela informa o tipo de exceção e descreve a operação que falhou. Comece por ela e depois leia para cima até o local do código imediatamente acima.
Um traceback simplificado, sem os marcadores de expressão, é:
Traceback (most recent call last):
File "scores.py", line 8, in <module>
report([])
File "scores.py", line 5, in report
avg = average(scores)
File "scores.py", line 2, in average
return sum(values) / len(values)
ZeroDivisionError: division by zero
Leia cada quadro como uma chamada ativa
Um quadro de chamada fornece o arquivo, o número da linha e a função, seguidos do código relevante. in <module> indica código de nível superior. A chamada mais antiga está no topo; a mais recente está na parte inferior. Ler de baixo para cima inverte essa ordem para que você possa perguntar o que falhou antes de perguntar como a execução chegou até ali.
- A linha 2 divide por
len(values). Para que esse divisor seja zero,valuesprecisa estar vazio. - A linha 5 chama
average(scores), então inspecione oscorespassado porreport. - A linha 8 fornece
[], a lista vazia. Isso explica como o divisor zero chegou ao cálculo.
A linha que falhou e a causa estão relacionadas, mas podem estar em lugares diferentes. Substituir a divisão sem decidir o que uma lista vazia deve significar ocultaria a questão de projeto. O código chamador deve evitar uma lista vazia ou a função precisa ter um comportamento definido para uma entrada vazia.
Use os destaques sem depender de sua forma exata
O Python 3.11 e versões posteriores podem acrescentar os marcadores ^ e ~ para mostrar a parte de uma expressão que falhou. Eles ajudam quando uma linha contém várias operações. O destaque exato varia entre versões, mas os locais das chamadas e a mensagem de exceção ainda indicam a ordem de leitura.
Um traceback pode conter quadros da biblioteca padrão ou de um pacote instalado. Encontre o quadro mais abaixo no seu próprio código e inspecione os argumentos que você forneceu ali. Um quadro de biblioteca é uma pista de localização; ele não prova que a biblioteca contém o defeito.
Reconheça um relatório de sintaxe
Um erro de sintaxe no script que está sendo iniciado impede sua execução. O Python informa um arquivo, uma linha, um marcador e uma mensagem sem o cabeçalho habitual de traceback. Por exemplo, falta o sinal de dois-pontos em if True; total = sum([1, 2, 3] deixa um parêntese aberto.
O local marcado é onde o Python percebeu que não conseguia interpretar a sintaxe do código. Quando essa linha parecer válida, verifique se há aspas ou parênteses não fechados na linha anterior. Não há cadeia de chamadas em tempo de execução a acompanhar quando o script inicial não foi executado.
Uma execução comum para em sua primeira exceção não tratada. Corrija esse problema e execute novamente para revelar o que houver depois no programa.
Principais conclusões
- Leia primeiro o tipo e a mensagem da exceção, depois o quadro que falhou e seus chamadores.
- Os quadros vão da chamada ativa mais antiga no topo até a mais recente na parte inferior.
- Rastreie o valor incorreto para trás; sua origem pode anteceder a falha.
- Um erro de sintaxe no script inicial é informado antes que esse script seja executado.
2.2 Distinga nomes, tipos, valores e resultados incorretos
O nome de um erro delimita a busca, e sua mensagem acrescenta detalhes sobre os valores envolvidos. Um programa também pode estar errado sem lançar uma exceção, então verificar sua saída esperada continua fazendo parte da busca por erros.
Distinga três tipos de problema
| Tipo | O que acontece | Exemplo |
|---|---|---|
| Erro de sintaxe | O Python não consegue interpretar a sintaxe do código | Falta de dois-pontos após if |
| Erro em tempo de execução | A execução chega a uma operação inválida | Divisão por zero |
| Erro de lógica | A execução termina com o comportamento incorreto | Um laço pula um preço |
IndentationError é um tipo de SyntaxError. O corpo de uma função precisa de indentação; a ausência de um bloco indentado interrompe o código antes da execução. Inspecione também aspas ou parênteses não fechados e palavras reservadas escritas incorretamente.
Leia nomes e tipos com atenção
Um NameError significa que o programa não conhece um nome naquele ponto. Em prnt("hi"), a função embutida pretendida é print. Um nome também pode ser usado antes da atribuição, ou uma importação pode estar ausente.
Um TypeError significa que uma operação recebeu um tipo inadequado ou que uma chamada recebeu o número errado de argumentos. "Age: " + 30 mistura uma string e um inteiro. Escreva "Age: " + str(30) ou f"Age: {30}". Se area precisa de w e h, area(3) também é um TypeError, dessa vez por causa de um argumento ausente.
A mensagem distingue essas causas. Em can only concatenate str (not "int") to str, o Python informa os tipos envolvidos. Após age = input("Age: "), age é texto. age + 1 falha; para uma entrada inteira, int(age) + 1 realiza a operação aritmética.
Um ValueError significa que o tipo da entrada é aceito, mas seu valor é inadequado. int("12.5") recebe uma string, que é um tipo de entrada aceito, mas o texto não representa um número inteiro na forma exigida. math.sqrt(-1) é outro valor inadequado para um cálculo com números reais.
Verifique posições, chaves e atributos
| Exceção | Exemplo | Causa |
|---|---|---|
IndexError | [4, 6, 10][3] | O último índice válido é 2 |
KeyError | {"apples": 3}["pears"] | O dicionário não tem "pears" |
ZeroDivisionError | 10 / 0 | O divisor é zero |
AttributeError | math.sqroot(16) | O módulo não tem sqroot |
ModuleNotFoundError | import mathh | Nenhum módulo é encontrado com esse nome |
ImportError | from math import square_root | O módulo não tem esse nome importado |
Divisores iguais a zero também provocam falhas com // e %. Uma posição de lista ausente e uma chave de dicionário ausente são consultas diferentes, embora ambas envolvam pedir algo que não existe.
Detecte um resultado que parece plausível
def total_price(prices):
total = 0
for i in range(1, len(prices)):
total += prices[i]
return total
Para [4, 6, 10], isso retorna 16 em vez de 20. Não há exceção: os índices 1 e 2 existem, então toda operação é válida. O laço começa tarde demais e deixa de incluir o índice 0. O erro se torna visível quando você compara a saída com o total pretendido.
Para uma exceção desconhecida, procure seu tipo e a mensagem geral na documentação oficial do Python. Remova seus próprios caminhos e valores de entrada de uma busca; eles raramente explicam a regra geral.
Principais conclusões
- Erros de sintaxe impedem a análise do código; erros em tempo de execução ocorrem durante a execução; erros de lógica produzem comportamento incorreto.
NameErrordiz respeito a uma associação;TypeErrordiz respeito a um tipo ou a argumentos de chamada.ValueErrordiz respeito a um valor inadequado fornecido em um tipo aceito.IndexError,KeyErroreAttributeErrorreferem-se a diferentes tipos de consulta.- Comparar a saída com um resultado esperado pode revelar um erro de lógica sem traceback.
3Encontre a causa de um defeito
Um resultado incorreto fica mais fácil de investigar quando você consegue reproduzi-lo e dizer exatamente como ele difere do resultado esperado. Você transformará essa observação em uma suposição testável, delimitará a busca a um pequeno trecho de código e inspecionará os valores relevantes. Impressões, tabelas de rastreamento e controles do depurador oferecem diferentes maneiras de observar a execução. Após uma correção pontual, verificar casos que funcionavam antes ajuda a revelar qualquer comportamento afetado pela alteração.
3.1 Teste uma causa antes de alterar o código
Um defeito é uma diferença entre o comportamento pretendido e o comportamento real. A depuração encontra a causa e a corrige. Uma sessão útil de depuração transforma uma observação em uma explicação testável e depois altera o código com base em evidências.
Faça a falha se repetir
Registre uma entrada específica e o resultado que ela produz. Para a função de preços que começa seu laço no índice 1, use [4, 6, 10]. O total esperado é 20; o total real é 16. Esses valores concretos permitem verificar se cada investigação ainda diz respeito ao mesmo defeito.
A diferença é 4, e o resultado real é igual a 6 + 10. Uma hipótese específica é que o primeiro preço é ignorado. Essa hipótese prevê algo que você pode inspecionar: o laço nunca passará pelo índice 0.
Siga seis etapas
- Reproduza: encontre uma entrada que mostre o defeito de forma consistente.
- Declare os resultados esperado e real: calcule o resultado esperado de forma independente.
- Formule uma hipótese: indique uma possível causa e o comportamento previsto por ela.
- Teste-a: inspecione valores com impressões, uma tabela de rastreamento ou um depurador.
- Corrija a causa: faça uma alteração pontual.
- Teste novamente: execute a entrada que falhou e os casos que funcionavam antes.
Exiba o índice dentro do laço. Para essa entrada, os únicos índices são 1 e 2, confirmando a previsão. Começar range em 0 corrige o item ignorado. Percorrer diretamente os preços elimina a necessidade de controlar um índice:
def total_price(prices):
total = 0
for price in prices:
total += price
return total
A função corrigida retorna 20 para [4, 6, 10], 5 para [5] e 0 para []. O caso com um item é útil porque o laço com defeito retornava anteriormente 0 para ele.
Adicionar 4 a cada resposta corrigiria o total observado, mas deixaria a causa. Para [5], essa correção improvisada retornaria 4; o valor ausente depende da entrada. Verifique se uma correção proposta explica todas as evidências antes de mantê-la.
Delimite uma busca maior
Divisão e conquista verifica os valores que alimentam o resultado incorreto em um ponto durante a execução. Se um valor necessário já estiver errado, inspecione o código anterior que o produziu. Se os valores necessários ainda estiverem corretos, acompanhe seu processamento posterior. Repita dentro da seção restante.
Você também pode chamar uma função isoladamente ou remover temporariamente código não relacionado. Um exemplo mínimo reproduzível mantém o código mais curto e a menor entrada que ainda falham. Para um problema visto inicialmente em centenas de registros, encontrar um ou dois registros que o provoquem facilita cada inspeção.
Verifique o que os dados realmente são
Um 5 exibido pode ser a string "5". "5" + "3" produz "53", enquanto a adição numérica resulta em 8. Inspecione type(value) para verificar o tipo. Use repr(value) para revelar espaços e caracteres de nova linha que uma impressão comum pode ocultar, como em ' Ada'.
Explique o código em voz alta, linha por linha, incluindo o que cada laço realmente percorre e o que cada função retorna. Isso é depuração com pato de borracha: explicar uma etapa muitas vezes revela a diferença entre a operação pretendida e a escrita.
Altere uma causa por vez para poder relacionar o resultado à edição. Teste novamente os casos que funcionavam antes, assim como aquele que falhou.
Principais conclusões
- Reproduza o defeito e registre os resultados esperado e real antes de editar.
- Uma hipótese útil prevê algo que você pode inspecionar.
- Corrija a causa com uma alteração e depois teste novamente os casos que falharam e os que funcionavam antes.
- Use um exemplo mínimo ou valores intermediários para delimitar a busca.
- Verifique tipos e caracteres ocultos quando os valores parecerem enganosos.
3.2 Observe a mudança de valores com impressões e um depurador
A resposta final de um programa oculta o caminho que a produziu. Impressões temporárias, uma tabela de rastreamento e um depurador permitem ver esse caminho e verificar uma hipótese específica sobre onde ele dá errado.
Exiba um valor em um momento conhecido
Identifique a saída de diagnóstico para que um número tenha um significado claro. O Python 3.8 e versões posteriores aceitam esta forma de f-string:
print(f"{i=} {total=}")
Para o laço de preços com defeito, coloque-a imediatamente antes de total += prices[i]. Com os preços [4, 6, 10], ela exibe:
i=1 total=0
i=2 total=6
Esses são os totais antes de cada adição. Após a primeira impressão, o laço soma 6; após a segunda, soma 10. Uma impressão após a adição mostraria totais diferentes. Decida qual momento responde à sua pergunta antes de interpretar a saída.
Rastreie o mesmo código à mão
Uma tabela de rastreamento registra variáveis passo a passo. Para esse laço:
Índice i | Preço somado | Total antes | Total depois |
|---|---|---|---|
| 1 | 6 | 0 | 6 |
| 2 | 10 | 6 | 16 |
Não há iteração para o índice 0. Acompanhe os valores reais de início e de parada escritos no código, mesmo quando você já sabe o que o laço deveria fazer. Uma tabela ajuda a separar o que você espera do que acontece.
Pause em um ponto de interrupção
Um depurador pausa um programa em execução para permitir que você inspecione seu estado atual. Um ponto de interrupção marca um lugar para pausar. Em editores como Thonny e VS Code, clique ao lado de um número de linha e inicie o modo de depuração; no IDLE, clique com o botão direito na linha e escolha “Set Breakpoint”.
Após a pausa, escolha uma etapa com base na questão que está investigando:
| Controle | Efeito |
|---|---|
| Executar sem entrar na função | Executa a linha atual, incluindo uma função chamada |
| Entrar na função | Entra em uma função chamada naquela linha |
| Sair da função | Conclui esta função e pausa após o retorno |
| Continuar | Executa até outro ponto de interrupção ou até o programa terminar |
Se o código chamador passar argumentos corretos, mas receber uma resposta incorreta, entre na função. Se você só precisa ver o resultado retornado, execute a chamada sem entrar na função. Inspecione os valores antes e depois da linha que pode criar o erro.
Use o depurador de texto do Python
Insira breakpoint() imediatamente antes do código que você quer inspecionar. Com a configuração padrão do depurador, executar o programa inicia o pdb naquele ponto. Digite estes comandos no prompt (Pdb):
p totalexibetotal.nexecuta a próxima linha sem entrar na função.sentra em uma chamada de função.ccontinua a execução.qencerra a sessão de depuração.
Esses são comandos do depurador, não linhas para copiar no seu programa.
Distinga a saída exibida dos resultados retornados
Uma função pode exibir o número correto e ainda retornar None porque não tem return total. Inspecione o valor recebido pelo código chamador quando a falha ocorrer após a chamada da função. Ver a saída correta dentro da função não comprova que o código chamador recebeu aquele número.
Use impressões para obter evidências rapidamente ao longo das iterações de um laço e o depurador para inspecionar um momento específico. Após corrigir o defeito, remova as impressões temporárias e as chamadas a breakpoint() para que o programa final seja executado normalmente.
Principais conclusões
- Identifique as impressões de depuração e observe se elas são executadas antes ou depois de uma atualização.
- Uma tabela de rastreamento acompanha as alterações reais das variáveis, uma etapa por vez.
- Pontos de interrupção pausam a execução; os controles de execução passo a passo determinam quanto código será executado em seguida.
breakpoint()abre o pdb por padrão;p,n,s,ceqsão comandos do depurador.- Inspecione valores retornados quando uma função exibe o resultado corretamente, mas o código chamador falha.
- Remova a saída temporária de diagnóstico e os pontos de interrupção após a correção.
4Verifique suas funções com testes
Verificações automatizadas permitem repetir um exemplo útil sempre que uma função muda. Você escreverá asserções com expectativas claras, escolherá entradas que atinjam os limites de uma regra e impedirá que os testes iniciem a aplicação que importam. A ferramenta padrão unittest dá a cada teste um nome e um resultado separado. Ler suas falhas e seus erros relaciona os testes à depuração: o teste reproduz um problema, e a investigação encontra sua causa.
4.1 Escreva verificações que falham quando uma suposição é falsa
Uma asserção declara algo que deve ser verdadeiro se seu código estiver funcionando corretamente. Ela pode interromper um defeito perto de sua origem ou comparar o resultado de uma função com o valor esperado.
Escreva uma condição e uma mensagem opcional
assert letter_grade(90) == "A", "90 should be an A"
O Python avalia a condição. Se letter_grade(90) retornar "A", a condição é verdadeira e a execução continua sem saída. Se retornar "B", o Python lança AssertionError com a mensagem 90 should be an A. O traceback aponta para a verificação que falhou.
Sem a mensagem, assert letter_grade(90) == "A" ainda verifica o resultado. A última linha da exceção contém AssertionError, enquanto o traceback mostra a expressão. Uma mensagem útil indica a expectativa que falhou; ela deve ajudar você a localizar a regra verificada.
Interrompa cedo um erro interno
Suponha que um cálculo interno exija uma lista não vazia:
def average(values):
assert len(values) > 0, "values must not be empty"
return sum(values) / len(values)
Agora, uma lista vazia aciona a asserção antes da divisão. A verificação documenta a suposição interna da função e aponta diretamente para sua violação. Ela não atribui uma média a uma lista vazia nem decide como uma aplicação deve responder à ausência de dados do usuário.
Mantenha ativa a validação de entrada do usuário
A opção -O do Python remove instruções assert. Portanto, um programa que depende de uma asserção para rejeitar entradas externas pode se comportar de forma diferente quando otimizado. Verifique entradas do usuário e outros dados externos com o fluxo de controle comum:
def require_score(score):
if score < 0 or score > 100:
raise ValueError("score must be 0 to 100")
return score
Aqui, raise ValueError(...) informa um valor inadequado. A instrução if continua sendo executada sob -O. Escolha uma asserção para uma suposição interna de programação e uma verificação de entrada ativa para dados externos que podem estar incorretos.
Evite uma verificação que nunca pode falhar
assert é uma instrução, não uma função. Escreva a condição e a mensagem separadas por vírgula:
assert x > 10, "x too small"
Escrever assert (x > 10, "x too small") cria uma tupla de dois itens. Uma tupla não vazia é considerada verdadeira mesmo quando seu primeiro item é False, então a asserção passa. O Python pode avisar que essa asserção é sempre verdadeira. Parênteses em torno apenas de uma condição, como em assert (x > 10), não criam essa tupla; o problema é a vírgula dentro dos parênteses.
Use == para comparação. assert total = 20 é sintaxe inválida porque = atribui um valor.
Compare números com a precisão pretendida
Para um número de ponto flutuante calculado, use uma verificação de proximidade:
import math
assert math.isclose(0.1 + 0.2, 0.3)
Uma comparação exata falha aqui porque a soma de ponto flutuante armazenada difere um pouco de 0.3. Um teste automatizado simples aplica a mesma ideia de asserção a uma função: assert total_price([4, 6, 10]) == 20. O valor esperado vem da regra da função, então a verificação pode revelar uma implementação incorreta.
Principais conclusões
assert condition, messagelançaAssertionErrorquando sua condição é falsa.- Asserções podem verificar suposições internas e resultados esperados de funções.
- Use
ife uma mensagem de erro ou exceção para entradas externas;-Oremove asserções. - Uma vírgula dentro de
assert (condition, message)cria uma tupla considerada verdadeira. - Use
==para igualdade emath.isclosepara comparar números de ponto flutuante calculados.
4.2 Escolha testes que possam revelar um defeito
Um teste unitário verifica uma pequena unidade de código, geralmente uma função, com uma entrada escolhida e um resultado esperado. Testes automatizados facilitam a repetição das mesmas verificações após edições, ajudando a detectar defeitos que voltam em comportamentos que funcionavam antes.
Comece pela especificação
Suponha que uma função de classificação de notas deva retornar "A" para notas de pelo menos 90, "B" para notas de pelo menos 80, mas abaixo de 90, e "C" abaixo de 80. A primeira condição com defeito é score > 90.
Os testes devem cumprir várias funções:
- Casos típicos verificam entradas comuns dentro de cada faixa, como 95 e 85.
- Casos limite verificam os valores em que uma regra muda, como 90 e 89, ou 80 e 79.
- Casos extremos verificam entradas como zero ou uma coleção vazia quando as regras da função as permitem.
Calcule a resposta esperada a partir dessas regras. Copiar a resposta atual do programa para um teste pode preservar seu erro.
Coloque os testes ao lado do módulo
Salve os testes em test_grade.py e a função em grade.py. A inicialização da aplicação em grade.py deve ficar sob a condição de execução principal __main__, para que importar a função não inicie o programa.
from grade import letter_grade
def test_letter_grade():
assert letter_grade(95) == "A"
assert letter_grade(90) == "A"
assert letter_grade(85) == "B"
Para a regra com defeito, a primeira asserção passa. A segunda falha porque 90 não é maior que 90; a execução para ali, antes da terceira asserção nessa função. Alterar a primeira condição para score >= 90 corrige esse limite. Execute todos os testes novamente para verificar o caso afetado e o comportamento anterior.
Garanta que os testes sejam executados
Definir uma função de teste não a chama. Sem um executor de testes, adicione test_letter_grade() no final do arquivo de teste e execute python test_grade.py. Para usar pytest, instale-o com python -m pip install pytest e execute pytest test_grade.py no terminal. Ele encontra as funções de teste e informa seus resultados. Não inclua chamadas manuais quando um executor gerenciar os testes.
Um teste que apenas chama letter_grade(90) não faz comparação. Ele pode terminar com sucesso enquanto a resposta retornada está errada; só falha se a chamada lançar uma exceção inesperada. Uma asserção relaciona a chamada ao comportamento exigido.
Teste valores retornados e propriedades aleatórias
Uma função que apenas exibe um total retorna None. Portanto, uma comparação como assert print_total([4, 6]) == 10 falha mesmo que a tela mostre 10. Faça o cálculo retornar sua resposta e deixe que main cuide da exibição.
Resultados aleatórios exigem uma expectativa diferente. Não se pode exigir que uma função de dado sem semente retorne uma face exata em todas as chamadas. Verifique se cada uma de 100 chamadas retorna um valor permitido de 1 a 6 ou defina uma semente antes de uma sequência reproduzível. Passar no teste de limites verifica os limites para essas chamadas; isso não comprova que todas as faces sejam igualmente prováveis.
Quando um novo defeito aparecer, adicione um teste que falhe por causa dele antes de corrigir a função. Mantenha esse teste de regressão para que edições posteriores verifiquem novamente o mesmo comportamento. Testes que passam comprovam o que aconteceu nos casos testados; outras entradas ainda podem revelar outro defeito.
Principais conclusões
- Escolha os resultados esperados a partir da especificação, independentemente da implementação.
- Teste entradas comuns, limites das regras e casos extremos relevantes.
- Funções de teste precisam de uma chamada ou de um executor; definições sozinhas não fazem verificações.
- Asserções devem comparar resultados retornados com o comportamento pretendido.
- Teste propriedades adequadas da saída aleatória ou prepare uma sequência reproduzível.
- Mantenha um teste para cada novo defeito encontrado para ajudar a detectar regressões.
4.3 Execute e interprete um arquivo de teste unittest
O Python inclui unittest, uma ferramenta de testes que descobre testes, executa-os e informa resultados separados. Ela não precisa ser instalada com pip. Sua estrutura de classe pode ser usada como modelo enquanto você aprende a testar funções.
Escreva métodos de teste independentes
Para uma função em grade.py, coloque isto em test_grade.py:
import unittest
from grade import letter_grade
class TestLetterGrade(unittest.TestCase):
def test_top(self):
self.assertEqual(letter_grade(95), "A")
def test_boundary(self):
self.assertEqual(letter_grade(90), "A")
def test_low(self):
self.assertEqual(letter_grade(50), "C")
if __name__ == "__main__":
unittest.main()
TestLetterGrade herda as ferramentas de teste de unittest.TestCase. Cada teste é um método cujo nome começa com test. self identifica o objeto de teste atual; mantenha-o na lista de parâmetros de cada método e antes das chamadas aos métodos de asserção. Você pode copiar essa estrutura sem projetar suas próprias classes.
Por padrão, os testes são executados em ordem alfabética dos nomes de seus métodos, então test_boundary vem antes de test_low e test_top. Faça cada teste fornecer a própria entrada necessária e funcionar independentemente dos testes anteriores.
Escolha um método de asserção
| Método | Verificação |
|---|---|
self.assertEqual(actual, expected) | Os valores são iguais |
self.assertNotEqual(a, b) | Os valores são diferentes |
self.assertTrue(condition) | Uma condição é verdadeira |
self.assertFalse(condition) | Uma condição é falsa |
self.assertIn(item, collection) | O item pertence à coleção |
self.assertAlmostEqual(a, b) | Por padrão, a diferença arredondada a 7 casas decimais é zero |
Prefira assertEqual(a, b) a assertTrue(a == b) ao comparar valores: o relatório de falha mostra os dois valores. Os métodos do unittest também mantêm suas verificações quando o Python é executado sob -O.
Para verificar uma exceção esperada, coloque a chamada dentro de assertRaises:
def test_score_too_high(self):
with self.assertRaises(ValueError):
letter_grade(120)
Esse teste exige que a regra da função rejeite essa nota com ValueError. Um retorno sem exceção faz essa verificação falhar; uma exceção diferente que não seja de asserção é um erro.
Execute e leia o relatório
Execute python test_grade.py para executar seu unittest.main() protegido pela condição de execução principal. Ou execute python -m unittest para descobrir arquivos test*.py na pasta atual. python -m unittest -v exibe os nomes individuais dos testes e seus resultados.
No relatório resumido, . significa que o teste passou, F significa que uma asserção falhou e E significa que outra exceção ocorreu. Para uma função de classificação com score > 90, os três testes acima produzem F... O relatório de falha de test_boundary mostra 'B' != 'A': o resultado real é "B", enquanto o resultado esperado é "A". O resumo informa três testes e uma falha.
Antes de editar a função, verifique a expectativa do teste em relação às regras de classificação. Uma expectativa incorreta exige uma correção no teste. Se nenhum teste for executado, inspecione os nomes dos métodos, a condição de execução direta e o comando de inicialização. Evite também chamar seu arquivo de unittest.py, pois ele oculta o módulo padrão.
Principais conclusões
- Coloque métodos
testdentro de uma classe que herda deunittest.TestCase. - Use
self.assertEquale outros métodos de asserção para obter verificações e relatórios claros. assertRaisesverifica uma exceção esperada; comparações de números de ponto flutuante podem usarassertAlmostEqual.- Execute diretamente um arquivo de teste com condição de execução principal ou use
python -m unittestpara descoberta. - Leia
Fcomo uma asserção que falhou eEcomo outra exceção. - Verifique um teste que falhou em relação à especificação antes de alterar a função.
- Cada teste deve ser executado independentemente da ordem alfabética de execução.
Exercícios resolvidos de Módulos e depuração em Python
1Crie um programa com partes reutilizáveis
20 passos
- 1IntroduçãoComece com código que você pode reutilizar
Um programa pode chamar código da biblioteca padrão do Python, de outro arquivo que você escreveu ou de um pacote instalado. A primeira questão é qual nome o programa tem disponível após a importação.
Você acompanhará um cálculo desde a importação até seu valor retornado, depois organizará um módulo reutilizável e associará a instalação de um pacote ao seu interpretador. Ao longo do caminho, verifique se um resultado é um valor que você mantém ou uma alteração feita em uma lista existente.
- 2ExplicaçãoA importação escolhe o estilo de chamada
Essas importações disponibilizam a mesma função por meio de associações diferentes:
import math print(math.sqrt(16))from math import sqrt print(sqrt(16))Ambos exibem
4.0. O primeiro criamath, então a chamada usa um ponto. O segundo criasqrt, então não precisa de prefixo de módulo. Usar o estilo de chamada errado lançaNameErrorquando aquele nome não foi associado. - 3ExemploCalcule a hipotenusa
Com
import math, um triângulo retângulo com lados 3 e 4 pode usar:length = math.sqrt(3**2 + 4**2)Os quadrados são 9 e 16. Sua soma é 25, e
math.sqrt(25)retorna5.0. O Python avalia o argumento antes de chamar a função.O prefixo também informa ao leitor de onde veio
sqrt. Uma importação com curinga oculta essa origem e pode substituir nomes existentes, como uma variável chamadae. - 4DesafioAcompanhe a associação do nome alternativo
Um script recém-iniciado executa estas linhas. O que acontece na última linha e como isso pode ser corrigido?
import statistics as st print(statistics.mean([4, 8, 12]))---reveal---
Ele lança
NameErrorporque a importação associast, nãostatistics. Usest.mean([4, 8, 12]), que retorna8. Um nome alternativo seleciona a associação neste programa; ele não cria também o nome original do módulo. - 5ExemploArredonde para obter recipientes inteiros suficientes
Uma parede precisa de tinta para 23 metros quadrados. Cada lata cobre 10 metros quadrados.
import math cans = math.ceil(23 / 10)A divisão resulta em
2.3;ceilresulta em3, latas inteiras suficientes.round(2.3)resulta em apenas2.Para entradas negativas, para baixo ainda significa em direção ao infinito negativo:
math.floor(-2.5)é-3, enquantoint(-2.5)corta em direção a zero e resulta em-2. - 6Ideia-chaveEscolha a comparação adequada ao número
Números de ponto flutuante calculados podem diferir um pouco de seus valores esperados em forma decimal:
0.1 + 0.2 == 0.3 # False math.isclose(0.1 + 0.2, 0.3) # TrueUse uma verificação de proximidade para essa comparação aproximada. Além disso, as entradas permitidas de uma função continuam sendo relevantes:
math.sqrt(-1)lançaValueError.A função embutida
roundnão precisa ser importada. Para um valor exatamente na metade, ela escolhe o inteiro par mais próximo:round(2.5)é2, eround(3.5)é4. - 7ExplicaçãoEscolha o resultado aleatório de que precisa
Após
import random, escolha uma chamada de acordo com sua função:randint(1, 6)escolhe um inteiro incluindo 6.randrange(1, 6)escolhe um inteiro excluindo 6.random()fornece um número de ponto flutuante de 0,0 até 1,0, excluindo o extremo superior.choice(items)escolhe um item.sample(items, k)escolhekposições sem reposição.
Duas posições selecionadas podem conter o mesmo valor. Portanto, amostrar
[1, 1]nas duas posições resulta em valores 1 repetidos. - 8DesafioMantenha a lista embaralhada
Que valor
namescontém no final e como a última linha deve mudar para manter a lista?names = ["Mina", "Luis", "Jo"] names = random.shuffle(names)---reveal---
Ele contém
None.shufflealtera a lista existente e depois retornaNone; a atribuição substitui a associação com a lista por esse valor de retorno. Userandom.shuffle(names)em uma linha própria. A nova ordem da lista é aleatória, mas seu comportamento de retorno é fixo. - 9ConexãoRepita uma simulação e encontre outras ferramentas
Chame
random.seed(42)antes de gerar valores para repetir a mesma sequência na mesma versão do Python. Isso facilita a reprodução de uma falha aleatória. Usesecretspara tokens de segurança.Outros módulos padrão resolvem tarefas cotidianas:
statistics.mean([2, 4, 9])resulta em5, emedianresulta em4.datetime.date.today()fornece a data de hoje.sys.argvlista o nome do script em execução seguido de seus argumentos de linha de comando. - 10ExemploColoque uma função reutilizável em um módulo
Salve isto como
geometry.py:import math def circle_area(radius): """Return the circle's area.""" return math.pi * radius ** 2Um script na mesma pasta pode executar
import geometrye depoisprint(geometry.circle_area(2)). Ele exibe12.566370614359172.Importe o arquivo sem
.py. A primeira string da função é sua docstring, quehelp(geometry.circle_area)pode exibir. - 11ExplicaçãoUma importação executa instruções de nível superior
Suponha que
geometry.pytermine comprint(circle_area(2)), fora de uma função. Sua primeira importação exibe a área como efeito colateral.O Python executa instruções no nível do módulo em ordem. Definir
circle_areacria a função sem executar seu corpo; chamá-la dentro do print de nível superior executa o cálculo. Uma segunda importação no mesmo processo reutiliza o módulo carregado. Um interpretador interativo também mantém essa versão carregada após a edição do arquivo, até que você o reinicie. - 12ExemploInicie somente durante uma execução direta
Deixe a função reutilizável acima deste bloco:
def main(): print(circle_area(2)) if __name__ == "__main__": main()Uma execução direta define
__name__como"__main__", entãomain()é executada. Uma importação o define como"geometry", então a inicialização é ignorada.As definições continuam disponíveis em ambos os casos. Isso permite que um teste importe
circle_areasem produzir a saída comum do programa. - 13DesafioMantenha a função disponível para quem a importa
Um arquivo define
convertdentro de seu blocoif __name__ == "__main__":. Um novo processo de teste importa o arquivo e não consegue encontrarconvert. Que organização permite tanto a reutilização quanto a inicialização por execução direta?---reveal---
Mova
def convert(...)para fora da condição e mantenha apenas a inicialização do programa dentro dela. Durante a importação, a condição é falsa, então uma definição dentro dela é ignorada. Mover a definição a torna disponível sem iniciar a aplicação. - 14DicaVerifique o arquivo encontrado por uma importação
O Python verifica módulos embutidos e depois procura em
sys.path, em ordem. A pasta do script normalmente aparece antes das pastas da biblioteca. Portanto, umrandom.pylocal pode ocultar orandomque você pretendia importar.Renomeie esse arquivo para algo como
dice.py. Para suas próprias funções auxiliares, use nomes válidos comomy_tools.pye mantenha-as ao lado do script de iniciante que as importa. Um arquivo fora do caminho de busca não é encontrado por uma importação comum. - 15ExemploInstale no terminal, importe no Python
No terminal, execute:
python -m pip install cowsayDepois, o código Python pode usar:
import cowsay cowsay.cow("Hello, Ada")O pacote exibe uma vaca feita de texto e um balão de fala.
pipinstala código, enquantoimportcria a associação usada pelo seu programa. O comando de instalação não é uma instrução Python; em um prompt>>>, ele produz sintaxe inválida. - 16ExplicaçãoUse o pip pertencente ao Python em execução
Se um script é executado com
python3, instale por meio do mesmo comando:python3 -m pip install cowsay python3 game.py-m pipseleciona o pip pertencente àquele interpretador. No Windows, seu comando pode serpy.Se o editor não conseguir importar um pacote instalado com sucesso, compare o interpretador selecionado nele com o usado no terminal. Uma instalação bem-sucedida em um Python não instala o pacote em todos os outros.
- 17DesafioDistinga o nome de instalação do nome de importação
A documentação de um pacote diz para instalar
Pillowe importarPIL. A instalação funciona para o Python que você executa, masimport Pillowfalha. O que deve mudar e por quê?---reveal---
Use
import PIL. O nome da distribuição usado pelo pip pode ser diferente do nome de importação Python que ela fornece. Como já foi confirmado que o interpretador é o mesmo, repetir o comando de instalação sem alterações manteria esse erro específico de nome. - 18DicaInspecione um pacote antes de alterá-lo
python -m pip listmostra as versões instaladas.show requestsfornece a versão e a localização de um pacote;install --upgrade requestso atualiza, euninstall requestso remove. Cada comando curto vem depois depython -m pip.Verifique o nome exato no PyPI e a documentação do projeto antes de instalar. Para um erro
externally-managed-environment, use um ambiente virtual para o projeto; não force uma instalação comsudo pip install. - 19DesafioPreveja uma importação e uma chamada posterior
Um novo módulo
sizes.pycontém este código. Outro script executafrom sizes import batchese depoisprint(batches(17)). O que é exibido?import math def batches(n): return math.ceil(n / 5) if __name__ == "__main__": print(batches(12))---reveal---
Apenas
4é exibido. A importação cria a função, mas pula o print protegido pela condição. A chamada posterior divide 17 por 5 e depois arredonda 3,4 para cima, para 4. A associação demathdentro desizesestá disponível para sua função. - 20RecapitulaçãoAcompanhe o código da origem até a chamada
Escolha uma importação que corresponda aos nomes que você quer usar. Verifique o que uma chamada de biblioteca retorna, incluindo um possível
Nonede uma operação que altera uma lista.Mantenha as definições reutilizáveis disponíveis durante a importação e coloque a inicialização da aplicação sob a condição de execução principal. Para código instalado, associe o pip ao interpretador em execução e verifique o nome de importação documentado. Quando uma chamada falha, esses detalhes ajudam a distinguir uma associação ausente, um atributo ausente e uma instalação ausente.
2Acompanhe uma falha até sua causa
11 passos
Salve esta aula para seguir cada Lição Interativa passo a passo.
- 1IntroduçãoUse o relatório como evidência
- 2ExemploUma lista vazia chega a uma divisão
- 3ExplicaçãoEncontre o quadro mais recente
- 4ExemploAcompanhe o argumento de volta pelos chamadores
- 5DesafioLocalize um argumento inadequado
- 6ExplicaçãoDistinga gramática, tipos e valores
- 7ExplicaçãoConsultas diferentes produzem erros diferentes
- 8ExemploUm total incorreto pode terminar normalmente
- 9DesafioExplique uma adição que falhou
- 10DesafioUse um quadro de biblioteca sem culpar a biblioteca
- 11RecapitulaçãoEscolha o próximo lugar para investigar
3Investigue um defeito das evidências à correção
14 passos
Salve esta aula para seguir cada Lição Interativa passo a passo.
- 1IntroduçãoFaça o resultado incorreto se repetir
- 2ExemploDeclare os resultados esperado e real
- 3ExplicaçãoTorne a suposição testável
- 4ExemploExiba antes da atualização
- 5ExemploRastreie as adições à mão
- 6DesafioInterprete uma impressão após a atualização
- 7ExemploCorrija a própria iteração
- 8ConexãoTeste novamente entradas de formatos diferentes
- 9DicaReduza uma falha maior
- 10DesafioVerifique um valor que parece numérico
- 11ExplicaçãoPause na chamada que você precisa inspecionar
- 12DesafioEscolha o comando de execução passo a passo
- 13DesafioInvestigue uma impressão correta e um retorno incorreto
- 14RecapitulaçãoMantenha a correção ligada às evidências
4Transforme um defeito de limite em um teste duradouro
16 passos
Salve esta aula para seguir cada Lição Interativa passo a passo.
- 1IntroduçãoCrie uma verificação reproduzível para o comportamento esperado
- 2ExplicaçãoUma asserção declara uma expectativa
- 3DesafioEncontre uma verificação que não pode falhar
- 4Ideia-chaveMantenha ativas as verificações de entrada externa
- 5ExemploEscolha entradas em torno da mudança de classificação
- 6ExemploEscreva uma função de teste que importa o código
- 7DicaExecute as verificações que você definiu
- 8DesafioCorrija um valor limiar e preserve a verificação
- 9ConexãoTeste o resultado retornado ou uma propriedade válida
- 10ExemploUse o modelo de classe unittest
- 11ExplicaçãoEscolha a asserção para o resultado
- 12ExemploLeia uma verificação de classificação que falhou
- 13DesafioDistinga uma falha de um erro
- 14DicaVerifique o que a descoberta realmente executou
- 15DesafioUse um relatório para planejar a correção
- 16RecapitulaçãoMantenha um teste para o comportamento corrigido
Questões de Módulos e depuração em Python
Conjunto 1: Módulos, escolhas de depuração e resultados de testes10 Questões de Prática
Questão 1 de 10
import statistics as st, a próxima linha é statistics.mean([3, 7]). Não há outras importações ou atribuições. O que explica o erro?Mostrar respostaOcultar resposta
st nesse script. Ela não associa também statistics, então usar o nome original do módulo sem qualificação lança NameError. Apenas mean([3, 7]) também falharia, pois importar um módulo com um nome alternativo não importa diretamente os nomes de suas funções.Questão 2 de 10
Qual é o valor de tickets após este código?
import random
tickets = [11, 22, 33]
tickets = random.shuffle(tickets)
Mostrar respostaOcultar resposta
shuffle reordena a lista existente e depois retorna None. A atribuição substitui a associação de tickets por esse valor de retorno, então ele deixa de se referir à lista. Para manter a associação, chame random.shuffle(tickets) em uma linha própria, sem atribuir o resultado.Questão 3 de 10
double e depois contém a linha sem proteção print(double(6)). Outro script o importa duas vezes em uma execução. Quantas vezes essa linha exibe um resultado?Mostrar respostaOcultar resposta
Questão 4 de 10
python -m pip show cowsay informa um local no Python B, e o editor não consegue importar cowsay. O que você deve fazer primeiro?Mostrar respostaOcultar resposta
Questão 5 de 10
Mostrar respostaOcultar resposta
Questão 6 de 10
Esta função deveria somar todas as quantidades. Que tipo de problema explica o resultado de total([2, 5, 8])?
def total(values):
result = 0
for i in range(1, len(values)):
result += values[i]
return result
Mostrar respostaOcultar resposta
Questão 7 de 10
Mostrar respostaOcultar resposta
Questão 8 de 10
assert (score >= 0, "negative score"). Ela passa quando score é -3. Qual alteração corrige a verificação e mantém a validação ativa sob -O?Mostrar respostaOcultar resposta
-O ainda a removeria. Um if que lança ValueError verifica a entrada externa tanto em execuções comuns quanto em otimizadas.Questão 9 de 10
test_discount, que contém duas asserções, mas nunca a chama. Você executa esse arquivo com python test_discount.py; não há código de um executor de testes. Por que nenhuma asserção é verificada?Mostrar respostaOcultar resposta
test_discount() em um arquivo executado diretamente ou deixe um executor de testes descobri-la. O Python não remove asserções apenas porque o nome do arquivo começa com test_; o problema aqui é a falta de execução.Questão 10 de 10
self.assertEqual(lookup("x"), 7). A chamada a lookup lança um KeyError inesperado antes de retornar. Qual resultado resumido marca esse teste?Mostrar respostaOcultar resposta
KeyError inesperado impede que a asserção de igualdade compare valores retornados, então o unittest registra um erro, mostrado como E. Uma asserção de igualdade falsa produziria F. Deixar de alcançar uma comparação não faz um teste passar quando ocorre uma exceção inesperada.Conjunto 2: Importações, pistas de erro e primeiras verificações10 Questões de Prática
Salve esta aula para ver todas as respostas e praticar cada conjunto.
Questão 1 de 10
import math antes de seu cálculo. Como ele deve chamar a função de raiz quadrada para 81?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 2 de 10
math.ceil(35 / 8) retorna e o que esse resultado representa?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 3 de 10
text_tools.py define shorten. Como outro arquivo na mesma pasta pode importá-la e chamá-la?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 4 de 10
>>> e digita pip install cowsay. Por que o Python informa sintaxe inválida?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 5 de 10
ZeroDivisionError. Seus quadros, de cima para baixo, mostram show([]), mean(data) e sum(values) / len(values). Qual sequência explica melhor a falha?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 6 de 10
int("7.25") lança?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 7 de 10
[9, 2, 5] e obter 16, mas retorna 7. Você suspeita que ela pula o primeiro item. Qual observação testaria essa ideia de forma mais direta?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 8 de 10
Quais duas linhas são exibidas?
total = 0
for n in [3, 8]:
print(f"{n=} {total=}")
total += n
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 9 de 10
O que acontece quando este código é executado normalmente?
count = 6
assert count < 5, "too many"
print("saved")
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 10 de 10
A regra prevê isenção até o peso 12, uma taxa de 4 para pesos de 13 a 18 e uma taxa de 7 acima de 18. Os testes para 5 e 20 passam. Quais resultados e alteração diagnosticam corretamente este código?
def fee(weight):
if weight < 12:
return 0
if weight <= 18:
return 4
return 7
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Conjunto 3: Rastreie causas entre módulos e testes10 Questões de Prática
Salve esta aula para ver todas as respostas e praticar cada conjunto.
Questão 1 de 10
O que este código exibe?
from math import sqrt as root
sqrt = 4
result = root(sqrt ** 2)
print(result, sqrt)
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 2 de 10
random.sample(["blue", "blue", "red"], 2). Por que o resultado pode conter dois valores "blue"?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 3 de 10
Um módulo contém este código. Um novo processo o importa e depois chama helper.answer(). O que acontece?
# helper.py
if __name__ == "__main__":
def answer():
return 42
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 4 de 10
run_report(), depois sua chamada parse(raw) e termina dentro de uma conversão de biblioteca com ValueError. Você inspeciona raw na chamada a parse e encontra "9x", embora a regra exija um texto que represente um número inteiro. Qual deve ser o próximo passo da investigação?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 5 de 10
result + 2, em que result é o valor de retorno da chamada. Por que ocorre um TypeError?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 6 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 7 de 10
answer = calculate(7). Você quer inspecionar a primeira linha dentro de calculate. Qual controle deve usar?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 8 de 10
charge(6) retorna 3, mas seu teste espera 3 e passa. Qual alteração cria um teste de regressão útil e corrige a regra?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 9 de 10
test_alpha esperando 4, mas recebendo 5, test_beta passando e test_gamma lançando um TypeError inesperado. Qual string compacta de progresso a execução alfabética padrão produz?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 10 de 10
unittest.TestCase tem check_empty com uma verificação de igualdade que falha e test_full com uma que passa. O executor informa um teste e OK. Como fazer a verificação de comportamento ausente ser executada?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Conjunto 4: Combine decisões de importação, depuração e testes10 Questões de Prática
Salve esta aula para ver todas as respostas e praticar cada conjunto.
Questão 1 de 10
random.randint(2, 5) 50 vezes. Um novo teste define a mesma semente na mesma versão do Python e repete as mesmas chamadas. O que suas verificações devem esperar?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 2 de 10
beautifulsoup4 como a distribuição e bs4 como seu módulo. Um script importa bs4 com sucesso. Um colega quer alterar python -m pip install beautifulsoup4 para instalar bs4, de modo que os nomes sejam iguais. Igualar esses nomes é uma razão válida para essa alteração?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 3 de 10
display.py informa estas chamadas, de cima para baixo: start(" "), read_count(text) e depois int(text), terminando em ValueError. Qual sequência testa melhor a causa provável antes de uma correção?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 4 de 10
[6, 3, 2]. Quais resultados esperados e reais para duas novas entradas revelam o problema restante?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 5 de 10
Esta função exibe um produto correto, mas seu código chamador recebe um TypeError em area(4, 5) + 1. Onde você deve inspecionar e qual resultado explica a falha?
def area(w, h):
product = w * h
print(product)
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 6 de 10
O que acontece em uma execução comum, sem otimização?
import math
assert math.isclose(0.1 + 0.2, 0.3)
assert 0.1 + 0.2 == 0.3
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 7 de 10
pick() retorna um inteiro aleatório de 3 a 7, incluindo os dois extremos. pair_total() soma duas chamadas a pick(). Um teste que afirma que todo total sem semente é igual a 10 às vezes falha, embora ambas as funções funcionem corretamente. Qual verificação por resultado corresponde ao intervalo exigido para o total?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 8 de 10
reserve(n) deve aceitar contagens inteiras de 0 a 8 e lançar ValueError fora desse intervalo. Testes que usam assertRaises(ValueError) informam F para -1 porque nenhuma exceção ocorre e E para 9 porque ocorre um TypeError. Um teste para 2 passa. Que comportamento a correção deve fornecer?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 9 de 10
random.py e importa random, mas falha porque random.randint está ausente. Qual primeira alteração resolve o conflito de nomes?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 10 de 10
assert total == 10 para um cálculo interno e assert age >= 0 para uma entrada do usuário. Ele é executado sob -O com total igual a 9 e age igual a -1. O que acontece com essas verificações e qual alteração mantém a validação de entrada exigida?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Travou em uma questão sua? Ajuda com Tarefas
Termos-Chave
A
Amostragem sem reposição
["A", "A", "B"] pode selecionar as duas posições que contêm "A".Asserção
AssertionError quando é falsa.assert count == 3 verifica um resultado interno em relação à contagem que você espera independentemente.AssertionError
"wrong total" direciona a atenção para o teste que comparou o total calculado.B
Biblioteca padrão
math para cálculos e random para simulações, como o lançamento de dados.C
Cadeia de chamadas
report até uma função average mostra como uma lista chegou à função que dividiu pelo seu comprimento.Caminho de busca de módulos
sys.path que o Python verifica após seus módulos embutidos ao procurar importações.ModuleNotFoundError quando o script o importa.Caso extremo
Caso limite
Código de nível superior
print pode produzir saída durante a importação, mesmo quando quem importa não chama nenhuma função.Condição de execução principal
if __name__ == "__main__": que executa seu corpo na execução direta e o ignora durante uma importação comum.main() permite que testes importem as funções do módulo sem iniciar sua aplicação.D
Defeito
Depuração
Depuração com impressões
print(f"{i=} {total=}") identifica o índice do laço e o total no momento inspecionado.Depurador
calculate para inspecionar seus argumentos quando o código chamador recebe uma resposta incorreta.Descoberta de testes
test*.py do unittest.python -m unittest pode executar arquivos correspondentes sem listar cada um em um comando.Distribuição
beautifulsoup4 é instalada com esse nome, mas sua documentação orienta usuários de Python a importar bs4.Divisão e conquista
E
Entrar na função
result = convert(text) quando o valor retornado está errado e o texto de entrada parece correto.Erro de lógica
Erro de sintaxe
if ready pode ser corrigido acrescentando os dois-pontos exigidos antes do bloco indentado.Erro de teste
E pode ocorrer quando uma função lança TypeError antes que a verificação de igualdade receba um valor.Erro em tempo de execução
Especificação
Exceção
average antes que seu código chamador exiba a média calculada.Executar sem entrar na função
Exemplo mínimo reproduzível
F
Falha de teste
F pode mostrar que a função retornou "B" quando a regra verificada exigia "A".H
Hipótese
I
Importação com curinga
* para trazer os nomes públicos de um módulo para o programa atual, o que pode ocultar suas origens e substituir associações existentes.from math import * pode substituir uma variável chamada e pela constante matemática.ImportError
from solicita um nome indisponível em um módulo, embora o Python tenha encontrado o próprio módulo.from math import square_root aponta para o nome solicitado, não para uma instalação ausente.IndexError
names[4] sugere verificar o comprimento da lista e os limites do laço que produziram 4.Interpretador
K
KeyError
"price" pode revelar que o dicionário atual nunca recebeu a entrada de preço esperada.M
Método de teste
test, permitindo que o unittest descubra e execute suas verificações.test_limit pode verificar um limite separadamente do teste de uma entrada comum.Módulo
.py, cujas funções e variáveis podem ser importadas para uso em outro programa.math fornece sqrt, para que um programa possa calcular raízes quadradas sem definir essa função.N
NameError
sqrt após apenas import math significa que a função precisa do prefixo math. nesse ponto.Nome alternativo
as para um módulo ou objeto importado; a importação associa esse nome no programa que o importa.stats em import statistics as stats permite que o script chame stats.mean([1, 3, 8]).Notação com ponto
math.sqrt(25) encontra a função sqrt dentro do módulo math já importado.Número pseudoaleatório
random.randint(1, 6) pode representar o resultado de um dado em uma simulação de jogo de tabuleiro.O
Opção de otimização
-O altera a execução do Python removendo instruções assert, então a validação obrigatória não pode depender dessas instruções.Operação no próprio objeto
random.shuffle(cards) altera cards e retorna None, então não precisa de atribuição.P
Pacote
pip
python3 -m pip show cowsay solicita ao pip associado a python3 que mostre os detalhes de instalação desse pacote.Piso
math.floor arredonda em direção ao infinito negativo quando necessário.math.floor(-3.2) difere da conversão com int(-3.2).Ponto de interrupção
PyPI
Q
Quadro de chamada
R
Rastreamento de exceção
Registro de nomes semelhantes com erros de digitação
Regressão
S
Semente
Sombreamento de módulo
random.py local pode deixar um script sem a função randint da biblioteca.String de documentação
help().circle_area pode explicar o argumento de raio e a área retornada ao código chamador.T
Tabela de rastreamento
TestCase
unittest.TestCase pode chamar self.assertEqual dentro de seus métodos de teste nomeados.Teste unitário
Teto
math.ceil fornece esse arredondamento para cima.TypeError
"Score: " + 8 identifica uma string e um inteiro que não podem ser unidos por essa expressão.V
Validação de entrada
if score < 0 permanece ativa quando o Python é executado com as asserções removidas.ValueError
int("6.2") significa que a string não descreve um número inteiro na forma exigida.Sobre esta aula
Crie seus próprios Materiais de Estudo
Transforme suas anotações, slides e documentos em Guias de Estudo, Questões de Prática, Flashcards e mais 6 ferramentas.