Como escrever funções em Python eficiente

Python

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:

  1. calcula o subtotal,
  2. decide o desconto,
  3. aplica o desconto,
  4. prepara o e-mail,
  5. envia o e-mail,
  6. 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:

  1. calcule o subtotal,
  2. descubra a taxa de desconto,
  3. aplique o desconto,
  4. envie o e-mail,
  5. 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_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.

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

0 comments:

Postar um comentário