Você criou sua primeira função em Python. Ela roda, não dá erro, devolve o resultado certo… missão cumprida.
Duas semanas depois, você abre o arquivo de novo e pensa:
“Quem foi que escreveu isso? O que essa função faz mesmo?”
Esse é um problema muito comum, tanto para quem está começando quanto para profissionais experientes.
Escrever funções legíveis não tem nada a ver com ser “gênio” ou “programador avançado”. Tem a ver com outra coisa bem mais simples: comunicar bem. Ou seja:
- deixar claro o que a função faz;
- deixar claro o que ela espera receber;
- deixar claro o que ela devolve;
- facilitar a vida da próxima pessoa que vai ler o código (que muitas vezes é você mesmo no mês seguinte).
A ideia deste texto é justamente essa: mostrar como escrever funções que parecem um bom texto de jornal, e não um enigma que ninguém consegue decifrar.
Dê nomes às funções como se estivesse explicando a um amigo
Pense no nome de uma função como o título de uma matéria: antes de ler o conteúdo, você já quer ter uma boa ideia do assunto.
Em programação, é a mesma lógica: o nome da função deve dizer, sozinho, o que ela faz.
Exemplo ruim
def proc(dados):
return sum(dados) / len(dados)
“proc”, “dados”… Nada disso diz muita coisa. Para entender, você é obrigado a olhar o corpo da função e “traduzir” mentalmente.
Exemplo bom
def calcular_media(numeros):
return sum(numeros) / len(numeros)
Aqui, o nome calcular_media já fala tudo:
- começa com um verbo (
calcular), indicando que a função faz uma ação;
- termina com o que está sendo calculado (
media).
O parâmetro numeros diz que a função espera uma coleção de números (por exemplo, uma lista).
Você não precisa ler o código para intuir o que acontece:
sum(numeros) → soma os valores;
len(numeros) → conta quantos valores existem;
- a divisão faz a média.
É como olhar para o título de uma reportagem e já ter uma boa noção do conteúdo antes de ler o texto completo.
💡 Pense assim:
Se o nome da função fosse a frase que você usaria para explicar o que ela faz para um colega no café, qual seria?
Use nomes descritivos também para os parâmetros
Parâmetros são as entradas da função — aquilo que você entrega para ela trabalhar. Se os nomes forem confusos, o uso da função fica nebuloso.
É como um formulário mal preenchido: se o campo está escrito “X” e você não sabe se é CPF, RG ou telefone, a chance de erro é grande.
Exemplo ruim
def desconto(p, r):
return p * (1 - r)
O que é p? Preço? Produto?
E r? Taxa? Desconto? Juros?
Exemplo bom
def aplicar_desconto(preco_original, taxa_desconto):
return preco_original * (1 - taxa_desconto)
Agora ficou claro:
preco_original → o preço original;
taxa_desconto → a taxa de desconto (por exemplo, 0.2 para 20%).
Quando alguém lê:
aplicar_desconto(100, 0.2)
já entende que está aplicando 20% de desconto em 100. O código se explica sozinho, sem comentário.
É como numa página de compra online: em vez de um campo chamado “valor X”, você vê “Preço do produto (em reais)”. Fica impossível confundir.
Uma função deveria fazer uma coisa bem feita.
Quando ela faz duas, três, quatro coisas diferentes, vira um “trabalho interno” difícil de testar, de reaproveitar e de entender.
Pense numa pessoa no trabalho que precisa:
- atender cliente;
- fazer planilha;
- emitir nota fiscal;
- arrumar o estoque;
- responder e-mail.
Tudo isso ao mesmo tempo. Fica confuso e ineficiente.
Exemplo ruim: função faz tudo
def processar_pedido(itens, email_cliente, codigo_desconto):
# Calcular subtotal
subtotal = sum(item["preco"] * item["quantidade"] for item in itens)
# Aplicar desconto
if codigo_desconto "SAVE10":
desconto = 0.10
elif codigo_desconto "SAVE20":
desconto = 0.20
else:
desconto = 0
total = subtotal * (1 - desconto)
# Enviar e-mail
assunto = "Confirmação de Pedido"
corpo = f"Seu pedido totalizou R$ {total:.2f}"
enviar_email(email_cliente, assunto, corpo)
return total
Essa função:
- calcula o subtotal,
- decide o desconto,
- aplica o desconto,
- prepara o e-mail,
- envia o e-mail,
- devolve o total.
Ela é o equivalente digital de um funcionário sobrecarregado.
Exemplo bom: dividir o problema em etapas
def calcular_subtotal_pedido(itens):
return sum(item["preco"] * item["quantidade"] for item in itens)
def obter_taxa_desconto(codigo_desconto):
taxas_desconto = {"SAVE10": 0.10, "SAVE20": 0.20}
return taxas_desconto.get(codigo_desconto, 0)
def aplicar_desconto_ao_subtotal(subtotal, taxa_desconto):
return subtotal * (1 - taxa_desconto)
def enviar_email_confirmacao_pedido(email_cliente, total):
assunto = "Confirmação de Pedido"
corpo = f"Seu pedido totalizou R$ {total:.2f}"
enviar_email(email_cliente, assunto, corpo)
def processar_pedido(itens, email_cliente, codigo_desconto):
subtotal = calcular_subtotal_pedido(itens)
taxa_desconto = obter_taxa_desconto(codigo_desconto)
total = aplicar_desconto_ao_subtotal(subtotal, taxa_desconto)
enviar_email_confirmacao_pedido(email_cliente, total)
return total
Agora cada função tem um papel claro:
calcular_subtotal_pedido → calcula o subtotal;
obter_taxa_desconto → entende o código de desconto;
aplicar_desconto_ao_subtotal → aplica o desconto;
enviar_email_confirmacao_pedido → envia o e-mail;
processar_pedido → orquestra tudo.
A função principal lê como uma receita:
- calcule o subtotal,
- descubra a taxa de desconto,
- aplique o desconto,
- envie o e-mail,
- devolva o total.
Isso é muito mais fácil de testar (você testa cada pedacinho separadamente) e de manter.
Mesmo com bons nomes, algumas funções precisam de um pouco mais de contexto. É aí que entram as docstrings, aquelas “mini-documentações” logo abaixo da definição da função.
Elas servem para explicar:
- por que a função existe;
- o que cada parâmetro significa;
- o que a função devolve;
- exemplos de uso.
É como o manual de um eletrodoméstico: o aparelho você até usa sem ler tudo, mas a explicação facilita muito.
Exemplo
def calcular_custo_envio(peso_kg, distancia_km, entrega_expressa=False):
"""
Calcula o custo de envio com base no peso do pacote e na distância.
Args:
peso_kg (float): Peso do pacote em quilogramas.
distancia_km (float): Distância do envio em quilômetros.
entrega_expressa (bool): Se True, usa envio expresso (padrão: False).
Returns:
float: Custo total de envio em reais.
Exemplo:
>>> calcular_custo_envio(5.0, 100, entrega_expressa=True)
45.50
"""
taxa_base = 2.50
taxa_por_kg = 1.20
taxa_por_km = 0.15
multiplicador_expressa = 2.0 if entrega_expressa else 1.0
custo = (
taxa_base + (peso_kg * taxa_por_kg) + (distancia_km * taxa_por_km)
) * multiplicador_expressa
return round(custo, 2)
Repare que, sem ver o corpo da função, você já sabe:
- que ela calcula custo de frete;
- o que são
peso_kg, distancia_km e entrega_expressa;
- o que é devolvido (um número decimal com o custo);
- um exemplo concreto de uso.
Em muitos editores de código, essa docstring aparece quando você passa o mouse em cima da função ou quando digita o nome dela. É documentação que anda junto com o código, sem precisar abrir arquivos à parte.
Use nomes claros também dentro da função
Não adianta ter nomes bons para a função e para os parâmetros se, lá dentro, você usa variáveis chamadas tmp, x, y, res. Isso obriga quem lê a “traduzir” tudo de novo.
É como pegar uma planilha em que as colunas se chamam “A”, “B”, “C”, sem dizer se é “Data”, “Cliente”, “Valor”.
Exemplo ruim
def calc_imc(p, a):
a_m = a / 100
res = p / (a_m**2)
return round(res, 1)
Você até consegue entender, mas precisa pensar: p é peso, a é altura, res é o quê?
Exemplo bom
def calcular_imc(peso_kg, altura_cm):
altura_metros = altura_cm / 100
imc = peso_kg / (altura_metros**2)
return round(imc, 1)
Aqui, tudo é autoexplicativo:
peso_kg → peso em quilos;
altura_cm → altura em centímetros;
altura_metros → altura convertida para metros;
imc → índice de massa corporal.
Quem lê não precisa ficar “adivinhando” o que cada coisa significa. Isso reduz erros e torna o código muito mais amigável, inclusive para quem está aprendendo.
“Número mágico” é qualquer número que aparece no código sem explicação, como 7, 14, 5….
Quem lê fica se perguntando: “Por que 7? Por que não 6 ou 8?”.
São regras escondidas que só existem na cabeça de quem escreveu.
O ideal é transformar esses números em constantes com nome, para deixar claro o significado.
Exemplo ruim
def calcular_multa_atraso(dias_atrasado):
if dias_atrasado <= 7:
return dias_atrasado * 2
else:
return 14 + (dias_atrasado - 7) * 5
De onde vieram 7, 2, 14, 5? É multa de biblioteca? De estacionamento? Qual a regra?
Exemplo bom
def calcular_multa_atraso(dias_atrasado):
TAXA_DIARIA_PRIMEIRA_SEMANA = 2
PERIODO_CARENCIA_DIAS = 7
MULTA_BASE_APOS_CARENCIA = 14
TAXA_DIARIA_APOS_CARENCIA = 5
if dias_atrasado <= PERIODO_CARENCIA_DIAS:
return dias_atrasado * TAXA_DIARIA_PRIMEIRA_SEMANA
else:
dias_apos_carencia = dias_atrasado - PERIODO_CARENCIA_DIAS
return MULTA_BASE_APOS_CARENCIA + (dias_apos_carencia * TAXA_DIARIA_APOS_CARENCIA)
Agora a regra fica explícita:
- existe um período de carência (
PERIODO_CARENCIA_DIAS = 7 dias);
- nesse período, a multa é de 2 por dia (
TAXA_DIARIA_PRIMEIRA_SEMANA);
- depois disso, existe uma multa base (
MULTA_BASE_APOS_CARENCIA = 14);
- e um valor diário maior (
TAXA_DIARIA_APOS_CARENCIA = 5).
É como ler um contrato: os valores e condições estão claramente descritos, e não escondidos em anotações soltas.
Outra vantagem: se um dia o valor da taxa mudar, você altera um lugar só (a constante), em vez de caçar o número 5 ou 7 espalhado pelo código inteiro.
Use type hints para deixar tudo ainda mais claro
Type hints (anotações de tipo) são uma forma de dizer quais tipos de dados a função espera e qual tipo ela devolve.
Python é uma linguagem dinâmica (não obriga a declarar tipos), mas os type hints ajudam:
- a evitar erros de uso;
- a deixar o código mais claro;
- a permitir que ferramentas e IDEs detectem problemas antes de rodar.
É como um formulário bem preenchido:
- “Idade (em anos):” → você já sabe que ali deve ir um número inteiro;
- “Nome completo:” → sabe que é texto;
- “É assinante? (Sim/Não)” → uma escolha booleana.
Exemplo
def formatar_saudacao_usuario(nome_usuario: str, idade: int, eh_membro: bool = False) -> str:
status_assinatura = "membro" if eh_membro else "convidado"
return f"Olá {nome_usuario}, idade {idade}. Você é {status_assinatura}."
Aqui, os type hints dizem:
nome_usuario: str → espera uma string como nome;
idade: int → espera um número inteiro como idade;
eh_membro: bool = False → espera um valor verdadeiro ou falso, com padrão False;
-> str → a função devolve uma string.
Se você tentar passar, por exemplo, uma lista no lugar da idade, ferramentas de análise de código podem acusar o erro antes mesmo da execução.
Para projetos maiores, isso faz uma diferença enorme em qualidade e manutenção.
Conclusão
Escrever funções legíveis não é escrever o “código perfeito”.
É fazer escolhas que ajudam quem vem depois:
- nomes claros para funções, parâmetros e variáveis;
- funções curtas, cada uma com uma responsabilidade bem definida;
- docstrings que explicam o propósito e o uso;
- constantes no lugar de números soltos;
- type hints para deixar os tipos explícitos.
No fundo, é tratar seu código como um texto que será lido, relido e interpretado.
A melhor recompensa é ouvir alguém — ou você mesmo no futuro, abrir o arquivo, ver a função e dizer:
“Ah, entendi rapidinho o que isso faz.”
No próximo passo da sua jornada em Python, o mesmo raciocínio vale para classes e estruturas maiores: nomes bons, responsabilidades claras e código que conversa bem com quem lê.
Até lá, siga praticando: a cada função nova, pergunte-se:
“Se eu voltar aqui daqui a seis meses, vou entender isso sem sofrimento?”
Se a resposta for “sim”, você está no caminho certo.