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.
Mantenha as funções curtas e com uma única responsabilidade
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.
Explique o propósito com docstrings
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_kmeentrega_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.
Evite “números mágicos”: prefira constantes com nome
“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ãoFalse;-> 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.