Módulos e depuração em Python: testes unitários

Aprenda a aproveitar código existente e a fazer seu próprio código funcionar de forma confiável. Importe módulos da biblioteca padrão do Python, como 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

  1. Inclua código pronto em um programa com instruções import, use módulos da biblioteca padrão, como math e random, 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 import
      Use import, from … import e as corretamente, 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ão
      Use as principais funções de math e random, 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ódulos
      Divida o código em seus próprios arquivos .py, importe-os, evite conflitos de nomes e use if __name__ == "__main__": para que um arquivo possa ser executado e importado.
    • Instale e gerencie pacotes com pip
      Instale, inspecione, atualize e remova pacotes de terceiros do PyPI, importe-os com o nome correto e resolva os problemas comuns de instalação.
  2. 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 cima
      Encontre 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 causas
      Relacione 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.
  3. 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 passo
      Reproduza 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 depurador
      Observe como os valores mudam durante a execução do código, adicionando impressões, rastreando à mão ou pausando em pontos de interrupção.
  4. Use assert para 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ão unittest.
    • Verifique suposições com instruções assert
      Escreva instruções assert com 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ções
      Escreva 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 unittest
      Escreva testes com unittest.TestCase, execute-os e leia o relatório de testes aprovados, falhas e erros.

Guia de estudo de Módulos e depuração em Python

4 capítulos

1

Reutilize 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çãoNome criadoExemplo de chamada
import mathmathmath.sqrt(16)
from math import sqrtsqrtsqrt(16)
import statistics as statsstatsstats.mean([2, 4, 9])
from random import randint as rollrollroll(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
ErroO que inspecionar
ModuleNotFoundError de import mathhA grafia e a instalação do módulo
ImportError de from math import square_rootO 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 module cria uma associação para o módulo; acesse seu conteúdo com a notação com ponto.
  • from module import name cria a associação selecionada; as escolhe 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ãoResultadoSignificado
math.floor(7.9)7Maior inteiro que não supera o valor
math.ceil(2.1)3Menor inteiro que não é inferior ao valor
math.floor(-2.5)-3Para baixo significa em direção ao infinito negativo
int(-3.5)-3A conversão corta em direção a zero
round(2.5)2Um valor exatamente na metade é arredondado para o par
round(3.5)4O 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.

ChamadaResultado
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, int e round respondem a diferentes necessidades de arredondamento.
  • Use math.isclose ao comparar números de ponto flutuante calculados.
  • randint inclui ambos os extremos; randrange exclui seu limite de parada.
  • sample seleciona posições sem reposição, então valores duplicados na entrada podem se repetir.
  • shuffle altera a própria lista e retorna None.
  • 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 .py usando 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 terminalFunção
python -m pip listListar pacotes instalados e versões
python -m pip show requestsMostrar detalhes, incluindo o local de instalação
python -m pip install --upgrade requestsAtualizar 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 requestsRemover 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 pip com 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.
2

Leia 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.

  1. A linha 2 divide por len(values). Para que esse divisor seja zero, values precisa estar vazio.
  2. A linha 5 chama average(scores), então inspecione o scores passado por report.
  3. 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
TipoO que aconteceExemplo
Erro de sintaxeO Python não consegue interpretar a sintaxe do códigoFalta de dois-pontos após if
Erro em tempo de execuçãoA execução chega a uma operação inválidaDivisão por zero
Erro de lógicaA execução termina com o comportamento incorretoUm 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çãoExemploCausa
IndexError[4, 6, 10][3]O último índice válido é 2
KeyError{"apples": 3}["pears"]O dicionário não tem "pears"
ZeroDivisionError10 / 0O divisor é zero
AttributeErrormath.sqroot(16)O módulo não tem sqroot
ModuleNotFoundErrorimport mathhNenhum módulo é encontrado com esse nome
ImportErrorfrom math import square_rootO 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.
  • NameError diz respeito a uma associação; TypeError diz respeito a um tipo ou a argumentos de chamada.
  • ValueError diz respeito a um valor inadequado fornecido em um tipo aceito.
  • IndexError, KeyError e AttributeError referem-se a diferentes tipos de consulta.
  • Comparar a saída com um resultado esperado pode revelar um erro de lógica sem traceback.
3

Encontre 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
  1. Reproduza: encontre uma entrada que mostre o defeito de forma consistente.
  2. Declare os resultados esperado e real: calcule o resultado esperado de forma independente.
  3. Formule uma hipótese: indique uma possível causa e o comportamento previsto por ela.
  4. Teste-a: inspecione valores com impressões, uma tabela de rastreamento ou um depurador.
  5. Corrija a causa: faça uma alteração pontual.
  6. 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 iPreço somadoTotal antesTotal depois
1606
210616

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:

ControleEfeito
Executar sem entrar na funçãoExecuta a linha atual, incluindo uma função chamada
Entrar na funçãoEntra em uma função chamada naquela linha
Sair da funçãoConclui esta função e pausa após o retorno
ContinuarExecuta 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 total exibe total.
  • n executa a próxima linha sem entrar na função.
  • s entra em uma chamada de função.
  • c continua a execução.
  • q encerra 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, c e q sã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.
4

Verifique 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, message lança AssertionError quando sua condição é falsa.
  • Asserções podem verificar suposições internas e resultados esperados de funções.
  • Use if e uma mensagem de erro ou exceção para entradas externas; -O remove asserções.
  • Uma vírgula dentro de assert (condition, message) cria uma tupla considerada verdadeira.
  • Use == para igualdade e math.isclose para 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étodoVerificaçã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 test dentro de uma classe que herda de unittest.TestCase.
  • Use self.assertEqual e outros métodos de asserção para obter verificações e relatórios claros.
  • assertRaises verifica uma exceção esperada; comparações de números de ponto flutuante podem usar assertAlmostEqual.
  • Execute diretamente um arquivo de teste com condição de execução principal ou use python -m unittest para descoberta.
  • Leia F como uma asserção que falhou e E como 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

4 Lições Interativas

1

Crie um programa com partes reutilizáveis

20 passos
Escolha importações e funções de biblioteca, reutilize seu próprio código e associe pacotes instalados ao Python que os executa.
  1. 1
    Introduçã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.

  2. 2
    Explicaçã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 cria math, então a chamada usa um ponto. O segundo cria sqrt, então não precisa de prefixo de módulo. Usar o estilo de chamada errado lança NameError quando aquele nome não foi associado.

  3. 3
    ExemploCalcule 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) retorna 5.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 chamada e.

  4. 4
    DesafioAcompanhe 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 NameError porque a importação associa st, não statistics. Use st.mean([4, 8, 12]), que retorna 8. Um nome alternativo seleciona a associação neste programa; ele não cria também o nome original do módulo.

  5. 5
    ExemploArredonde 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; ceil resulta em 3, latas inteiras suficientes. round(2.3) resulta em apenas 2.

    Para entradas negativas, para baixo ainda significa em direção ao infinito negativo: math.floor(-2.5) é -3, enquanto int(-2.5) corta em direção a zero e resulta em -2.

  6. 6
    Ideia-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)   # True
    

    Use 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ça ValueError.

    A função embutida round não precisa ser importada. Para um valor exatamente na metade, ela escolhe o inteiro par mais próximo: round(2.5) é 2, e round(3.5) é 4.

  7. 7
    Explicaçã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) escolhe k posiçõ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.

  8. 8
    DesafioMantenha a lista embaralhada

    Que valor names conté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. shuffle altera a lista existente e depois retorna None; a atribuição substitui a associação com a lista por esse valor de retorno. Use random.shuffle(names) em uma linha própria. A nova ordem da lista é aleatória, mas seu comportamento de retorno é fixo.

  9. 9
    Conexã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. Use secrets para tokens de segurança.

    Outros módulos padrão resolvem tarefas cotidianas: statistics.mean([2, 4, 9]) resulta em 5, e median resulta em 4. datetime.date.today() fornece a data de hoje. sys.argv lista o nome do script em execução seguido de seus argumentos de linha de comando.

  10. 10
    ExemploColoque 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 ** 2
    

    Um script na mesma pasta pode executar import geometry e depois print(geometry.circle_area(2)). Ele exibe 12.566370614359172.

    Importe o arquivo sem .py. A primeira string da função é sua docstring, que help(geometry.circle_area) pode exibir.

  11. 11
    ExplicaçãoUma importação executa instruções de nível superior

    Suponha que geometry.py termine com print(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_area cria 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.

  12. 12
    ExemploInicie 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ão main() é 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_area sem produzir a saída comum do programa.

  13. 13
    DesafioMantenha a função disponível para quem a importa

    Um arquivo define convert dentro de seu bloco if __name__ == "__main__":. Um novo processo de teste importa o arquivo e não consegue encontrar convert. 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.

  14. 14
    DicaVerifique 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, um random.py local pode ocultar o random que você pretendia importar.

    Renomeie esse arquivo para algo como dice.py. Para suas próprias funções auxiliares, use nomes válidos como my_tools.py e 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.

  15. 15
    ExemploInstale no terminal, importe no Python

    No terminal, execute:

    python -m pip install cowsay
    

    Depois, 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. pip instala código, enquanto import cria 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.

  16. 16
    Explicaçã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 pip seleciona o pip pertencente àquele interpretador. No Windows, seu comando pode ser py.

    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.

  17. 17
    DesafioDistinga o nome de instalação do nome de importação

    A documentação de um pacote diz para instalar Pillow e importar PIL. A instalação funciona para o Python que você executa, mas import Pillow falha. 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.

  18. 18
    DicaInspecione um pacote antes de alterá-lo

    python -m pip list mostra as versões instaladas. show requests fornece a versão e a localização de um pacote; install --upgrade requests o atualiza, e uninstall requests o remove. Cada comando curto vem depois de python -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 com sudo pip install.

  19. 19
    DesafioPreveja uma importação e uma chamada posterior

    Um novo módulo sizes.py contém este código. Outro script executa from sizes import batches e depois print(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 de math dentro de sizes está disponível para sua função.

  20. 20
    Recapitulaçã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 None de 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.

2

Acompanhe uma falha até sua causa

11 passos
Leia quadros de chamada, reconheça exceções comuns e distinga uma falha de um resultado incorreto que não produz mensagem de erro.

Salve esta aula para seguir cada Lição Interativa passo a passo.

  1. 1
    IntroduçãoUse o relatório como evidência
  2. 2
    ExemploUma lista vazia chega a uma divisão
  3. 3
    ExplicaçãoEncontre o quadro mais recente
  4. 4
    ExemploAcompanhe o argumento de volta pelos chamadores
  5. 5
    DesafioLocalize um argumento inadequado
  6. 6
    ExplicaçãoDistinga gramática, tipos e valores
  7. 7
    ExplicaçãoConsultas diferentes produzem erros diferentes
  8. 8
    ExemploUm total incorreto pode terminar normalmente
  9. 9
    DesafioExplique uma adição que falhou
  10. 10
    DesafioUse um quadro de biblioteca sem culpar a biblioteca
  11. 11
    RecapitulaçãoEscolha o próximo lugar para investigar
3

Investigue um defeito das evidências à correção

14 passos
Reproduza um total incorreto, inspecione seu laço, faça uma correção pontual e use os controles do depurador para acompanhar uma chamada de função.

Salve esta aula para seguir cada Lição Interativa passo a passo.

  1. 1
    IntroduçãoFaça o resultado incorreto se repetir
  2. 2
    ExemploDeclare os resultados esperado e real
  3. 3
    ExplicaçãoTorne a suposição testável
  4. 4
    ExemploExiba antes da atualização
  5. 5
    ExemploRastreie as adições à mão
  6. 6
    DesafioInterprete uma impressão após a atualização
  7. 7
    ExemploCorrija a própria iteração
  8. 8
    ConexãoTeste novamente entradas de formatos diferentes
  9. 9
    DicaReduza uma falha maior
  10. 10
    DesafioVerifique um valor que parece numérico
  11. 11
    ExplicaçãoPause na chamada que você precisa inspecionar
  12. 12
    DesafioEscolha o comando de execução passo a passo
  13. 13
    DesafioInvestigue uma impressão correta e um retorno incorreto
  14. 14
    RecapitulaçãoMantenha a correção ligada às evidências
4

Transforme um defeito de limite em um teste duradouro

16 passos
Escreva asserções, escolha entradas que revelem problemas e use unittest para executar verificações independentes e interpretar seus resultados.

Salve esta aula para seguir cada Lição Interativa passo a passo.

  1. 1
    IntroduçãoCrie uma verificação reproduzível para o comportamento esperado
  2. 2
    ExplicaçãoUma asserção declara uma expectativa
  3. 3
    DesafioEncontre uma verificação que não pode falhar
  4. 4
    Ideia-chaveMantenha ativas as verificações de entrada externa
  5. 5
    ExemploEscolha entradas em torno da mudança de classificação
  6. 6
    ExemploEscreva uma função de teste que importa o código
  7. 7
    DicaExecute as verificações que você definiu
  8. 8
    DesafioCorrija um valor limiar e preserve a verificação
  9. 9
    ConexãoTeste o resultado retornado ou uma propriedade válida
  10. 10
    ExemploUse o modelo de classe unittest
  11. 11
    ExplicaçãoEscolha a asserção para o resultado
  12. 12
    ExemploLeia uma verificação de classificação que falhou
  13. 13
    DesafioDistinga uma falha de um erro
  14. 14
    DicaVerifique o que a descoberta realmente executou
  15. 15
    DesafioUse um relatório para planejar a correção
  16. 16
    RecapitulaçãoMantenha um teste para o comportamento corrigido

Questões de Módulos e depuração em Python

40 Questões de Prática em 4 conjuntos

Conjunto 1: Módulos, escolhas de depuração e resultados de testes10 Questões de Prática

Questão 1 de 10

Após 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?
  1. A função mean também exige import math.
  2. Apenas st foi associado; chame st.mean([3, 7]).
  3. Apenas mean foi associado; chame mean([3, 7]).
  4. O nome alternativo deve ser passado como statistics.mean(st).
Mostrar respostaOcultar resposta
Resposta: B
A forma com nome alternativo associa o módulo a 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)
  1. Uma nova lista embaralhada de três itens
  2. O primeiro item da lista embaralhada
  3. A lista original em sua ordem original
  4. None
Mostrar respostaOcultar resposta
Resposta: D
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

Um módulo auxiliar define 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?
  1. Uma vez, durante a primeira importação
  2. Duas vezes, uma para cada instrução de importação
  3. Nunca, pois importações apenas leem nomes de funções
  4. Uma vez, somente se o script chamar double depois
Mostrar respostaOcultar resposta
Resposta: A
A primeira importação executa as instruções de nível superior do módulo auxiliar, incluindo essa impressão. A segunda importação reutiliza o módulo carregado, então não executa a linha novamente. Importar envolve mais do que coletar nomes de funções: chamadas de nível superior sem proteção também são executadas.

Questão 4 de 10

Um editor executa um script com o Python A. O comando de terminal 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?
  1. Executar novamente a instalação pelo pip do Python B sem alterações.
  2. Substituir a importação pelo caminho da pasta do pacote.
  3. Executar -m pip install cowsay com o Python A.
  4. Mover o script para o diretório de bibliotecas do Python B.
Mostrar respostaOcultar resposta
Resposta: C
Os detalhes da instalação mostram que o pip atuou no Python B, enquanto o editor usa o Python A. Executar o pip por meio de A instala o pacote para o interpretador que precisa dele. Repetir o comando de B sem alterações afetaria a mesma outra instalação e não resolveria a diferença.

Questão 5 de 10

Um traceback tem três quadros. Onde você encontra primeiro o tipo e a mensagem da exceção?
  1. No primeiro quadro, ao lado do nome do script
  2. No cabeçalho acima do primeiro quadro
  3. Na linha com a chamada mais antiga
  4. Na última linha, abaixo dos quadros
Mostrar respostaOcultar resposta
Resposta: D
A última linha identifica a exceção e sua mensagem, fornecendo a primeira pista sobre a operação que falhou. O quadro inferior mostra então o local mais recente do código. Começar pelo quadro mais antigo informa como a execução começou, mas ainda não indica qual operação falhou.

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
  1. Um IndexError ocorre porque o laço alcança o índice 3.
  2. Um erro de lógica retorna 13 porque o índice 0 é ignorado.
  3. Um TypeError ocorre porque itens de lista não podem ser somados.
  4. Um erro de sintaxe ocorre porque range precisa de três argumentos.
Mostrar respostaOcultar resposta
Resposta: B
O laço percorre os índices 1 e 2, então soma 5 e 8 e retorna 13. Ambos os índices existem; o limite de parada excluído impede o acesso ao índice 3. O código é válido e executa, mas seu valor inicial pula a primeira quantidade, tornando isso um erro de lógica.

Questão 7 de 10

Um programa de processamento de dados que falha tem 300 linhas e 200 registros. O que constitui um exemplo mínimo reproduzível útil?
  1. O código completo com todos os registros e toda a saída de diagnóstico
  2. A resposta esperada com a entrada que falha totalmente removida
  3. O código mais curto e a menor entrada que ainda falham
  4. Um programa reescrito cuja saída já foi corrigida
Mostrar respostaOcultar resposta
Resposta: C
Um exemplo mínimo mantém a falha enquanto remove código e dados não relacionados. Isso facilita o acompanhamento dos valores que provocam o defeito e fornece a outras pessoas informações suficientes para reproduzi-lo. Remover a entrada que falha pode encurtar o exemplo, mas também pode remover a evidência necessária para investigar.

Questão 8 de 10

Uma verificação de entrada usa 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?
  1. if score < 0: raise ValueError("negative score")
  2. assert score >= 0, "negative score"
  3. assert (score < 0, "negative score")
  4. assert (score >= 0) == True
Mostrar respostaOcultar resposta
Resposta: A
A condição original é uma tupla não vazia, considerada verdadeira mesmo quando seu primeiro elemento é falso. Uma asserção escrita corretamente corrigiria esse erro de tupla, mas -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

Um arquivo de teste simples define 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?
  1. O Python remove todas as asserções de qualquer arquivo test_.
  2. Cada asserção precisa ser movida para cima da função.
  3. Definir a função não executa seu corpo.
  4. A função de teste precisa retornar True antes de ser chamada.
Mostrar respostaOcultar resposta
Resposta: C
Uma definição de função cria a função, mas não executa seu corpo. Chame 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

Um método unittest chama self.assertEqual(lookup("x"), 7). A chamada a lookup lança um KeyError inesperado antes de retornar. Qual resultado resumido marca esse teste?
  1. F, pois todo problema em um teste é uma falha de asserção
  2. E, pois outra exceção interrompeu a verificação
  3. ., pois nenhuma comparação de igualdade resultou em falso
  4. F, pois KeyError é o mesmo que AssertionError
Mostrar respostaOcultar resposta
Resposta: B
O 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

Um script contém apenas import math antes de seu cálculo. Como ele deve chamar a função de raiz quadrada para 81?
  1. sqrt(81)
  2. math(81).sqrt()
  3. math.sqrt(81)
  4. import.sqrt(81)
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 2 de 10

Um veículo de transporte comporta 8 pessoas. O que math.ceil(35 / 8) retorna e o que esse resultado representa?
  1. 5, os veículos necessários para todos
  2. 4, os veículos necessários para todos
  3. 4.375, os veículos que devem ser reservados
  4. 4, a quantidade de assentos que ficam vazios
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 3 de 10

Um arquivo chamado text_tools.py define shorten. Como outro arquivo na mesma pasta pode importá-la e chamá-la?
  1. import text_tools.py, depois text_tools.shorten("Hello")
  2. import shorten, depois shorten.text_tools("Hello")
  3. import text_tools, depois shorten.text_tools("Hello")
  4. import text_tools, depois text_tools.shorten("Hello")
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 4 de 10

Você está em um prompt Python >>> e digita pip install cowsay. Por que o Python informa sintaxe inválida?
  1. O pacote precisa ser importado antes de ser instalado.
  2. O comando de instalação deve ser executado no terminal.
  3. O nome do pacote deve ser escrito com a extensão .py.
  4. O comando de instalação precisa de uma atribuição Python.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 5 de 10

Um traceback termina em ZeroDivisionError. Seus quadros, de cima para baixo, mostram show([]), mean(data) e sum(values) / len(values). Qual sequência explica melhor a falha?
  1. A função show tentou dividir seu próprio nome por zero.
  2. Uma lista vazia chegou a mean, tornando seu divisor zero.
  3. A lista ficou vazia porque o traceback é exibido ao contrário.
  4. A soma era zero, o que sempre torna a divisão inválida.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 6 de 10

Qual exceção int("7.25") lança?
  1. TypeError
  2. NameError
  3. IndexError
  4. ValueError
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 7 de 10

Uma função deveria somar [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?
  1. Registrar os índices percorridos pelo laço de soma.
  2. Exibir novamente o total esperado após a chamada da função.
  3. Adicionar 9 ao resultado e verificar apenas essa mesma entrada.
  4. Renomear a variável do total e executar novamente sem inspeção.
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
  1. n=3 total=3, depois n=8 total=11
  2. n=3 total=0, depois n=8 total=0
  3. n=3 total=0, depois n=8 total=3
  4. n=0 total=3, depois n=3 total=8
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")
  1. Ele lança AssertionError com "too many".
  2. Ele exibe "saved" e depois informa a asserção.
  3. Ele atribui 5 a count e continua sem saída.
  4. Ele exibe "too many" e depois exibe "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
  1. Em 12: 0; em 18: 7. Altere o segundo teste para < 18.
  2. Em 12: 4; em 18: 7. Altere as duas comparações para <.
  3. Em 12: 0; em 18: 4. Mantenha as duas comparações.
  4. Em 12: 4; em 18: 4. Altere o primeiro teste para <= 12.
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)
  1. 16.0 4
  2. 4.0 4.0
  3. Ele lança TypeError porque sqrt é um inteiro.
  4. 4.0 4
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 2 de 10

Um sorteio usa random.sample(["blue", "blue", "red"], 2). Por que o resultado pode conter dois valores "blue"?
  1. A amostragem sempre repõe o primeiro item antes de sortear novamente.
  2. As duas entradas azuis ocupam posições diferentes na entrada.
  3. O valor 2 solicita duas cópias idênticas de um item escolhido.
  4. A amostragem remove valores duplicados somente após retorná-los.
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
  1. Ele retorna 42 porque o Python carrega todas as definições primeiro.
  2. NameError: o Python nunca define __name__ durante a importação.
  3. AttributeError: a importação pulou a definição da função.
  4. Ele retorna None porque a condição altera os valores de retorno.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 4 de 10

Um traceback começa com sua chamada 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?
  1. Rastrear onde raw se tornou "9x" antes de parse ser chamada.
  2. Substituir int(raw) por int(raw[0]) para essa entrada produzir um número.
  3. Tratar o quadro da biblioteca como prova de defeito e editar sua conversão.
  4. Manter a entrada e reinstalar a biblioteca porque o chamador já executou.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 5 de 10

Uma função exibe 18, mas não tem instrução de retorno. Seu código chamador avalia então result + 2, em que result é o valor de retorno da chamada. Por que ocorre um TypeError?
  1. result é None, que não pode ser somado a 2.
  2. result é a string "18" porque a impressão converte retornos.
  3. result é 18, mas somar dois inteiros é inválido aqui.
  4. result não está definido porque toda impressão apaga sua associação.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 6 de 10

Um fluxo lê, converte e totaliza dados. Em uma falha reproduzível, os valores necessários para o total já estão incorretos imediatamente após a conversão. A leitura isolada produz os valores brutos esperados. Qual é a próxima investigação mais pontual?
  1. Adicionar uma correção ao total final e deixar a conversão como está.
  2. Reescrever as três etapas juntas e comparar apenas a saída final.
  3. Executar a conversão isoladamente com a menor entrada bruta que falha.
  4. Inspecionar apenas a impressão final, onde a saída incorreta aparece.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 7 de 10

Um depurador está pausado em answer = calculate(7). Você quer inspecionar a primeira linha dentro de calculate. Qual controle deve usar?
  1. Executar sem entrar na função
  2. Entrar na função
  3. Sair da função
  4. Continuar a execução
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 8 de 10

A regra diz que uma viagem é gratuita para distâncias até 6 e custa 3 acima de 6. 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?
  1. Manter 3 em 6 e adicionar apenas um teste para uma viagem paga distante.
  2. Alterar a regra para cobrar em 6 e manter o teste existente.
  3. Remover a verificação em 6 e comparar apenas dois exemplos aprovados.
  4. Esperar 0 em 6, observar a falha, corrigir e executar os testes novamente.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 9 de 10

Um arquivo unittest tem 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?
  1. E.F
  2. F.E
  3. F.F
  4. .FE
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 10 de 10

Um 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?
  1. Renomear check_empty para test_empty e executar novamente.
  2. Adicionar return False ao final de check_empty.
  3. Mover check_empty para baixo de test_full na classe.
  4. Substituir assertEqual por print em check_empty.
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

Uma simulação define uma semente uma vez e depois chama 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?
  1. A sequência se repete, e todo resultado fica de 2 a 5.
  2. A sequência se repete, e todo resultado fica de 2 a 4.
  3. Cada resultado ocorre igualmente, e os quatro valores devem aparecer.
  4. Cada chamada repete o primeiro resultado porque a semente permanece definida.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 2 de 10

A documentação indica 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?
  1. Sim; toda importação bem-sucedida deve usar o nome de instalação da distribuição.
  2. Sim; importar bs4 prova que o pip instalou uma distribuição com esse nome.
  3. Não; a instalação segue o nome documentado da distribuição, que pode ser diferente.
  4. Não; o módulo deve primeiro ser renomeado para corresponder à distribuição atual.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 3 de 10

Uma nova execução de 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?
  1. Inspecionar apenas type(text); receber uma string basta para comprovar um erro de tipo.
  2. Inspecionar repr(text), confirmar a entrada em branco e definir como tratá-la.
  3. Mover a mesma conversão para start, para executá-la antes das outras chamadas.
  4. Substituir cada conversão que falha por zero, sem verificar o tratamento exigido.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 4 de 10

Uma função de total ignora o primeiro item. Uma correção improvisada adiciona 6 a cada total retornado e agora passa para [6, 3, 2]. Quais resultados esperados e reais para duas novas entradas revelam o problema restante?
  1. [9]: esperado 9, real 15; []: esperado 0, real 6.
  2. [9]: esperado 9, real 6; []: esperado 0, real 0.
  3. [9]: esperado 6, real 6; []: esperado 6, real 6.
  4. [9]: esperado 9, real 6; []: esperado 0, real 6.
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)
  1. Executar sem entrar na chamada; seu valor retornado é o inteiro 20.
  2. Executar sem entrar na chamada; seu valor retornado é a string "20".
  3. Executar sem entrar na chamada; seu valor retornado é None.
  4. Entrar na chamada; seu resultado exibido torna-se o valor de retorno.
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
  1. A primeira asserção passa; a segunda lança AssertionError.
  2. Ambas as asserções passam porque somas decimais são armazenadas exatamente.
  3. A primeira asserção falha; a segunda nunca é executada.
  4. Ambas as asserções falham, mas o programa continua até o fim.
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?
  1. Verificar 3 <= value <= 7 para cada total.
  2. Verificar 6 <= value < 14 para cada total.
  3. Verificar value == 10 para cada total.
  4. Verificar 6 <= value <= 14 para cada 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?
  1. Aceitar -1 normalmente; lançar ValueError em 9; manter 2 válido.
  2. Lançar ValueError em -1 e 9; manter 2 válido.
  3. Lançar ValueError em -1; manter TypeError em 9 e 2 válido.
  4. Aceitar -1 e 9 normalmente; lançar ValueError em 2.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 9 de 10

Um script se chama random.py e importa random, mas falha porque random.randint está ausente. Qual primeira alteração resolve o conflito de nomes?
  1. Instalar novamente o módulo random usando pip.
  2. Mover import random para depois da chamada a randint.
  3. Manter random.py e usar from random import randint.
  4. Renomear o script para draw_numbers.py.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 10 de 10

Um programa tem 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?
  1. Apenas a verificação do total desaparece; acrescente uma mensagem maior à asserção da idade.
  2. Ambas as verificações desaparecem; coloque a condição e a mensagem da idade em uma tupla.
  3. Ambas as verificações desaparecem; use if age < 0 e uma resposta de erro para a entrada.
  4. Apenas a verificação da idade desaparece; mova-a para uma função chamada test_age.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Travou em uma questão sua? Ajuda com Tarefas

Termos-Chave

60 Termos-Chave, cada um com sua definição

A

Amostragem sem reposição
A amostragem sem reposição escolhe posições diferentes da entrada, de modo que cada posição seja selecionada no máximo uma vez, mesmo que os valores se repitam.
A amostragem sem reposição de ["A", "A", "B"] pode selecionar as duas posições que contêm "A".
Asserção
Uma asserção verifica uma condição que deve ser verdadeira se o código estiver correto, continuando quando ela é verdadeira e lançando AssertionError quando é falsa.
Uma asserção como assert count == 3 verifica um resultado interno em relação à contagem que você espera independentemente.
AssertionError
Um AssertionError sinaliza uma asserção que falhou e pode incluir uma mensagem fornecida que explica qual expectativa a verificação não atendeu.
Um AssertionError com "wrong total" direciona a atenção para o teste que comparou o total calculado.

B

Biblioteca padrão
A biblioteca padrão é a coleção de módulos incluída no Python, que fornece ferramentas prontas sem uma instalação separada de pacotes.
A biblioteca padrão inclui math para cálculos e random para simulações, como o lançamento de dados.

C

Cadeia de chamadas
Uma cadeia de chamadas é a sequência de chamadas de função ativas que levou à operação atual, do chamador externo até a chamada mais recente.
Uma cadeia de chamadas de uma função report até uma função average mostra como uma lista chegou à função que dividiu pelo seu comprimento.
Caminho de busca de módulos
O caminho de busca de módulos é a lista ordenada de pastas em sys.path que o Python verifica após seus módulos embutidos ao procurar importações.
Um arquivo auxiliar fora do caminho de busca de módulos pode produzir ModuleNotFoundError quando o script o importa.
Caso extremo
Um caso extremo é uma entrada incomum em um limite extremo ou especial, como uma lista vazia, que pode revelar comportamentos que entradas comuns não detectam.
Um caso extremo com uma lista de um item pode revelar uma função de total que começa seu laço no segundo item.
Caso limite
Um caso limite é uma entrada de teste em um limite onde uma regra muda, facilitando a detecção de erros em comparações inclusivas e exclusivas.
Um caso limite na nota 90 verifica se a regra da classificação A usa a comparação inclusiva exigida.
Código de nível superior
O código de nível superior consiste em instruções no nível do módulo, fora dos corpos de funções, que são executadas em ordem quando um módulo é carregado pela primeira vez.
Um código de nível superior que chama 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
Uma condição de execução principal é um bloco if __name__ == "__main__": que executa seu corpo na execução direta e o ignora durante uma importação comum.
Uma condição de execução principal em torno de main() permite que testes importem as funções do módulo sem iniciar sua aplicação.

D

Defeito
Um defeito é um erro que faz o comportamento de um programa diferir do pretendido, incluindo respostas incorretas e falhas inesperadas.
Um defeito que ignora o primeiro preço pode ser reproduzido com uma lista de um item cujo total esperado é fácil de verificar.
Depuração
A depuração é o processo de reproduzir um comportamento inesperado, localizar sua causa, corrigi-la e verificar novamente o comportamento afetado.
A depuração de um laço que soma preços inclui observar os índices que ele percorre antes de alterar o valor inicial do laço.
Depuração com impressões
A depuração com impressões acrescenta saídas temporárias em locais selecionados do código para revelar valores, ordem de execução e se uma hipótese corresponde ao comportamento.
A depuração com impressões usando print(f"{i=} {total=}") identifica o índice do laço e o total no momento inspecionado.
Depurador
Um depurador pausa a execução de um programa e fornece controles para inspecionar valores atuais e avançar pelo código uma etapa por vez.
Um depurador permite que você entre em calculate para inspecionar seus argumentos quando o código chamador recebe uma resposta incorreta.
Descoberta de testes
A descoberta de testes encontra testes executáveis usando regras de nomes de arquivos e métodos, como o padrão de arquivo test*.py do unittest.
A descoberta de testes com python -m unittest pode executar arquivos correspondentes sem listar cada um em um comando.
Distribuição
Uma distribuição é um projeto Python instalável identificado por um nome de gerenciador de pacotes, que pode ser diferente do nome usado nas importações.
A distribuição beautifulsoup4 é instalada com esse nome, mas sua documentação orienta usuários de Python a importar bs4.
Divisão e conquista
A divisão e conquista delimita uma busca de depuração verificando valores intermediários necessários e acompanhando a seção em que eles começam a ficar incorretos.
A divisão e conquista direciona a atenção para a conversão se os dados brutos estão corretos, mas os valores convertidos já estão errados.

E

Entrar na função
Entrar na função é uma ação do depurador que acessa uma função chamada na linha atual, para que você possa inspecionar as etapas dentro dessa função.
Use entrar na função em result = convert(text) quando o valor retornado está errado e o texto de entrada parece correto.
Erro de lógica
Um erro de lógica faz um programa se comportar incorretamente mesmo quando suas instruções são executadas sem produzir um relatório de exceção.
Um erro de lógica em um laço que começa no índice 1 ignora o primeiro preço e produz um total baixo demais.
Erro de sintaxe
Um erro de sintaxe significa que o código viola a gramática do Python, como pela falta de dois-pontos, e impede que o script afetado comece normalmente.
Um erro de sintaxe após if ready pode ser corrigido acrescentando os dois-pontos exigidos antes do bloco indentado.
Erro de teste
Um erro de teste é um resultado de teste causado por uma exceção inesperada que não seja uma asserção que falhou, no teste ou no código que ele chama.
Um erro de teste marcado com 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
Um erro em tempo de execução é um problema detectado enquanto o código é executado, como uma operação inválida que lança uma exceção naquele momento.
Um erro em tempo de execução ocorre quando uma expressão de divisão válida é alcançada com um divisor cujo valor é zero.
Especificação
Uma especificação declara o comportamento que uma função deve fornecer, oferecendo uma base independente para entradas, resultados esperados e verificações válidas de teste.
A especificação de que notas de pelo menos 90 recebem A define a resposta esperada para um teste com exatamente 90.
Exceção
Uma exceção sinaliza um problema detectado durante a execução; uma exceção não tratada interrompe o programa e normalmente produz um traceback.
Uma exceção por divisão por zero pode interromper average antes que seu código chamador exiba a média calculada.
Executar sem entrar na função
Executar sem entrar na função é uma ação do depurador que executa a linha atual, incluindo funções chamadas, sem acompanhar suas linhas internas.
Use executar sem entrar na função em uma chamada de cálculo quando quiser inspecionar seu resultado retornado no código chamador.
Exemplo mínimo reproduzível
Um exemplo mínimo reproduzível é o menor código e a menor entrada que preservam uma falha para que as etapas relevantes possam ser examinadas diretamente.
Um exemplo mínimo reproduzível pode reduzir centenas de registros aos dois que ainda provocam a mesma falha.

F

Falha de teste
Uma falha de teste é um resultado de teste causado por uma asserção falsa, como um valor de retorno real diferente do valor esperado especificado.
Uma falha de teste marcada com F pode mostrar que a função retornou "B" quando a regra verificada exigia "A".

H

Hipótese
Uma hipótese é uma explicação específica proposta para um defeito que prevê uma observação que você pode verificar no programa em execução.
A hipótese de que um laço ignora seu primeiro item prevê que uma impressão de seus índices nunca mostrará 0.

I

Importação com curinga
Uma importação com curinga usa * 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.
A importação com curinga from math import * pode substituir uma variável chamada e pela constante matemática.
ImportError
Um ImportError pode ocorrer quando from solicita um nome indisponível em um módulo, embora o Python tenha encontrado o próprio módulo.
Um ImportError de from math import square_root aponta para o nome solicitado, não para uma instalação ausente.
IndexError
Um IndexError ocorre quando uma consulta a uma sequência solicita uma posição que não existe, como o índice 3 em uma lista de três itens.
Um IndexError de names[4] sugere verificar o comprimento da lista e os limites do laço que produziram 4.
Interpretador
Um interpretador é o programa que executa código Python; diferentes interpretadores instalados podem ter pacotes de terceiros diferentes disponíveis.
A escolha do interpretador do editor explica por que um pacote instalado para outro Python ainda pode estar ausente.

K

KeyError
Um KeyError ocorre quando uma consulta a um dicionário solicita uma chave ausente, de modo que o Python não consegue retornar um valor para essa chave por indexação.
Um KeyError para "price" pode revelar que o dicionário atual nunca recebeu a entrada de preço esperada.

M

Método de teste
Um método de teste é uma função dentro de uma classe de teste cujo nome começa com test, permitindo que o unittest descubra e execute suas verificações.
Um método de teste chamado test_limit pode verificar um limite separadamente do teste de uma entrada comum.
Módulo
Um módulo é uma unidade de código Python, geralmente um arquivo .py, cujas funções e variáveis podem ser importadas para uso em outro programa.
O módulo math fornece sqrt, para que um programa possa calcular raízes quadradas sem definir essa função.

N

NameError
Um NameError ocorre quando o Python não consegue encontrar uma associação para um nome usado em uma expressão, muitas vezes por causa de um erro de digitação ou de uma importação ausente.
Um NameError para sqrt após apenas import math significa que a função precisa do prefixo math. nesse ponto.
Nome alternativo
Um nome alternativo é o nome escolhido com as para um módulo ou objeto importado; a importação associa esse nome no programa que o importa.
O nome alternativo stats em import statistics as stats permite que o script chame stats.mean([1, 3, 8]).
Notação com ponto
A notação com ponto acessa um atributo nomeado dentro de um objeto ou módulo colocando um ponto entre o nome do objeto e o nome do atributo.
A notação com ponto em math.sqrt(25) encontra a função sqrt dentro do módulo math já importado.
Número pseudoaleatório
Um número pseudoaleatório é produzido por um algoritmo determinístico cuja sequência é projetada para parecer aleatória em usos como simulações.
Um número pseudoaleatório de 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
A 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.
Iniciar um script com a opção de otimização faz desaparecer uma verificação de idade negativa baseada apenas em asserção.
Operação no próprio objeto
Uma operação no próprio objeto altera um objeto existente, como uma lista, sem exigir um novo objeto para armazenar o conteúdo alterado.
A operação no próprio objeto random.shuffle(cards) altera cards e retorna None, então não precisa de atribuição.

P

Pacote
Um pacote agrupa módulos Python relacionados, normalmente dentro de uma pasta, para que programas possam organizar e reutilizar uma coleção maior de código.
Um pacote de terceiros pode fornecer vários módulos relacionados que um programa importa para tarefas diferentes.
pip
pip é uma ferramenta de instalação que baixa e gerencia distribuições Python de terceiros para o interpretador que a executa.
Executar python3 -m pip show cowsay solicita ao pip associado a python3 que mostre os detalhes de instalação desse pacote.
Piso
O piso de um número é o maior inteiro que não supera esse número; math.floor arredonda em direção ao infinito negativo quando necessário.
O piso de -3,2 é -4, então math.floor(-3.2) difere da conversão com int(-3.2).
Ponto de interrupção
Um ponto de interrupção marca um local onde um depurador pausa a execução, permitindo a inspeção antes de continuar com o código seguinte.
Um ponto de interrupção antes de uma atualização no laço permite comparar o total atual com o item prestes a ser somado.
PyPI
O PyPI é o Índice de Pacotes Python, um repositório público onde publicadores disponibilizam distribuições de terceiros para instalação.
A página de um pacote no PyPI fornece informações do projeto e documentação para verificar antes de instalá-lo.

Q

Quadro de chamada
Um quadro de chamada em um traceback identifica o local de uma chamada ativa por seu arquivo, número de linha e função, com a linha de código-fonte relevante.
O quadro mais abaixo pode mostrar uma divisão pelo comprimento de uma lista vazia, enquanto os quadros anteriores revelam qual chamador forneceu essa lista.

R

Rastreamento de exceção
Um rastreamento de exceção informa a cadeia de chamadas ativas no momento de uma exceção não tratada, terminando com o tipo e a mensagem da exceção.
Leia primeiro a última linha do traceback e depois acompanhe o quadro que falhou para cima, em direção à chamada original.
Registro de nomes semelhantes com erros de digitação
O registro de nomes semelhantes com erros de digitação consiste em publicar com um nome parecido para atingir pessoas que digitam incorretamente o nome de um projeto confiável ao instalar software.
Verificar a grafia exata de um projeto no PyPI ajuda a evitar esse tipo de registro ao copiar um comando de instalação.
Regressão
Uma regressão é um defeito introduzido em um comportamento que funcionava antes, muitas vezes após uma edição alterar código do qual outras funções ou testes dependem.
Uma regressão pode fazer um teste de total para um único item, que passava antes, falhar após uma alteração destinada a corrigir uma entrada vazia.

S

Semente
Uma semente inicializa um gerador pseudoaleatório, permitindo repetir a mesma sequência de chamadas na mesma versão do Python.
Definir a semente antes de uma simulação permite que um programador inspecione a mesma sequência novamente durante a depuração.
Sombreamento de módulo
O sombreamento de módulo acontece quando um módulo encontrado antes tem o nome do módulo pretendido e o oculta durante a busca de importação.
O sombreamento de módulo por um random.py local pode deixar um script sem a função randint da biblioteca.
String de documentação
Uma string de documentação é uma string no início de um módulo ou função que documenta sua finalidade e pode ser exibida por meio de help().
Uma docstring em circle_area pode explicar o argumento de raio e a área retornada ao código chamador.

T

Tabela de rastreamento
Uma tabela de rastreamento registra valores de variáveis em etapas sucessivas de execução, permitindo acompanhar um programa à mão e inspecionar suas alterações.
Uma tabela de rastreamento para um laço de acumulação mostra o total antes e depois da adição de cada item.
TestCase
Um TestCase é a classe-base do unittest para classes de teste, que fornece métodos de asserção e suporte para executar cada teste descoberto.
Uma classe que herda de unittest.TestCase pode chamar self.assertEqual dentro de seus métodos de teste nomeados.
Teste unitário
Um teste unitário é uma pequena verificação automatizada de uma unidade, como uma função, que compara seu comportamento para uma entrada escolhida com um resultado esperado.
Um teste unitário verifica se uma função de preço retorna 0 para uma lista vazia quando sua especificação exige esse resultado.
Teto
O teto de um número é o menor inteiro que não é inferior a esse número; math.ceil fornece esse arredondamento para cima.
Um cálculo de teto transforma 3,4 latas de tinta necessárias em quatro latas inteiras, suficientes para a área indicada.
TypeError
Um TypeError sinaliza um tipo inadequado para uma operação ou um número incorreto de argumentos em uma chamada de função, como um argumento obrigatório ausente.
Um TypeError de "Score: " + 8 identifica uma string e um inteiro que não podem ser unidos por essa expressão.

V

Validação de entrada
A validação de entrada verifica se dados externos atendem aos requisitos de um programa e responde a entradas inadequadas antes de usá-las.
A validação de entrada com if score < 0 permanece ativa quando o Python é executado com as asserções removidas.
ValueError
Um ValueError sinaliza que uma operação aceita o tipo da entrada, mas não pode usar seu valor específico, como um texto numérico inválido.
Um ValueError de int("6.2") significa que a string não descreve um número inteiro na forma exigida.

Sobre esta aula

Por StudeliRelatar um erro

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.

Começar