Decomposição, contratos e docstrings
Objetivo da aula
Projetar funções pequenas e documentar expectativas essenciais.
Regra desta aula: vamos assumir que você nunca viu este assunto antes. Nenhum termo importante será usado sem explicação e todo exemplo deve ser entendido linha por linha, não apenas copiado.
Começando do zero
Decompor é quebrar um problema grande em partes menores e nomeadas. Cada função deve ter uma responsabilidade que você consegue explicar em uma frase. Um contrato simples descreve o que ela espera receber, o que promete retornar e quais condições os dados devem satisfazer. Docstrings registram esse propósito perto do código.
Uma imagem mental para entender
Imagine organizar uma cozinha: alguém lava, alguém corta, alguém cozinha e alguém monta o prato. Se uma pessoa tenta fazer tudo ao mesmo tempo sem etapas, fica difícil localizar um problema. Funções separadas permitem raciocinar sobre uma parte de cada vez.
Conceitos essenciais, sem pular etapas
Responsabilidade única
Não é uma lei mecânica, mas uma boa pergunta: “esta função possui um motivo claro para mudar?”. Uma função de calcular média não precisa também pedir senha e salvar arquivo.
Contrato
Define entradas esperadas, saída e regras importantes. Exemplo: “recebe lista não vazia de números e retorna média”.
Pré-condição
É algo que deve ser verdadeiro antes de a função cumprir seu contrato, como uma lista conter pelo menos um valor.
Docstring
Texto logo após a definição, entre aspas triplas, usado para documentar propósito, parâmetros, retorno e restrições.
Funções pequenas combinadas
Um programa pode ter função para entrada, outra para regra e outra para apresentação. A coordenação acontece em uma camada principal.
Modelo mental
PROBLEMA → RESPONSABILIDADES → CONTRATOS → FUNÇÕES PEQUENAS → COMPOSIÇÃO
Exemplo 1 — primeiro veja o programa inteiro
def calcular_media(valores):
"""Retorna a média de uma lista não vazia de números."""
return sum(valores) / len(valores)
notas = [8.0, 7.5, 9.0]
media = calcular_media(notas)
print(f"Média: {media:.2f}")
Agora vamos ler linha por linha
- O nome explica a responsabilidade.
- A docstring registra a pré-condição “lista não vazia”.
- A função recebe dados e retorna resultado; não pergunta nada ao usuário.
- A apresentação fica fora, o que facilita testar a função isoladamente.
Saída esperada
Média: 8.17
Exemplo 2 — o mesmo conceito em outra situação
def validar_nota(nota):
"""Retorna True quando nota está entre 0 e 10, inclusive."""
return 0 <= nota <= 10
def calcular_situacao(media):
"""Retorna a situação para uma média já validada."""
if media >= 7:
return "aprovado"
return "reprovado"
Duas regras diferentes recebem nomes próprios. Isso permite testá-las independentemente e torna a leitura do programa principal mais próxima da linguagem do problema.
Experimento guiado
Não pule esta parte. Programação só começa a fazer sentido quando você prevê um resultado, executa e compara a previsão com o que realmente aconteceu.
- Pegue um trecho de código com entrada, cálculo e saída e identifique responsabilidades separáveis.
- Escreva uma frase de contrato antes de implementar cada função.
- Adicione docstring curta a uma função criada por você.
- Teste a função diretamente com valores conhecidos, sem depender de
input().
Mini desafio
Decomponha “calcular preço final” em duas funções: uma calcula desconto e outra aplica o desconto ao preço.
Solução comentada
def calcular_valor_desconto(preco, taxa):
"""Retorna o valor monetário correspondente à taxa de desconto."""
return preco * taxa
def calcular_preco_final(preco, taxa):
"""Retorna preço após desconto."""
desconto = calcular_valor_desconto(preco, taxa)
return preco - desconto
print(calcular_preco_final(100, 0.20))
Depois de executar a solução, altere pelo menos um valor e explique por que o novo resultado mudou. Se você só copiou e não consegue explicar cada linha, refaça o desafio em uma versão menor.
Erros comuns e por que acontecem
- Criar função para cada linha sem que exista uma responsabilidade real.
- Criar função que faz tudo: entrada, validação, cálculo, arquivo e apresentação.
- Documentar apenas a sintaxe e não a intenção ou restrições.
- Não testar funções isoladamente e descobrir erros somente no programa completo.
Cheque se você realmente entendeu
O que um contrato simples deve dizer?
Resposta: O que a função recebe, o que retorna e quais condições importantes existem.
Por que separar cálculo de input()?
Resposta: Para tornar a regra reutilizável e testável sem interação humana.
Docstring substitui nomes claros?
Resposta: Não. Ela complementa um design que já deve ser legível.
Resumo em linguagem simples
Nesta aula, o ponto central foi Decomposição, contratos e docstrings. Tente explicar o modelo PROBLEMA → RESPONSABILIDADES → CONTRATOS → FUNÇÕES PEQUENAS → COMPOSIÇÃO sem olhar a tela. Depois, escreva um exemplo próprio menor que os apresentados. Esse exercício de explicação é parte do aprendizado, não um extra.
Conexão com a próxima aula
Na próxima aula, Strings como sequências, vamos aproveitar exatamente o que foi construído aqui. Você não precisa memorizar tudo; precisa conseguir reconhecer o conceito e reconstruí-lo com raciocínio e testes.