O que é uma função, sem rodeios
Uma função é um trecho de código que recebe entrada, processa e devolve saída. O resto é detalhe. No dia a dia, eu vejo gente complicar o conceito e perder tempo tentando encaixar exemplos artificiais quando o problema real tá na assinatura da função, nos efeitos colaterais e em como os dados fluem entre módulos. O jeito mais rápido de entender é pela prática: uma função existe para esconder complexidade. Se você escreve um bloco de 20 linhas que faz o mesmo em dois lugares no código, transforme em função. Se o nome não é claro, refatore o nome. Mais nada.O que significa função quando as coisas dão errado
Eu tive um caso recente num projeto interno onde uma função retornava `None` em vez de levantar exceção quando um arquivo CSV vinha corrompido. O código chamador simplesmente continuava rodando, produzindo resultados errados em silêncio. A solução foi trocar o retorno por um `raise ValueError(f"Linha inválida na posição {i}")` e mapear o erro pra uma log line específica. Isso cortou o tempo de debugging de três horas para dez minutos. A lição prática é: funções devem ser hostis quando algo dá errado. Silêncio é o pior comportamento possível.Como construir funções úteis
O que significa função, na verdade, vai além da definição teórica. Uma função boa tem estes atributos: - Nome que descreve o comportamento, não a implementação. `carregar_cadastro_de_clientes` é melhor que `processa_dados`. - Parâmetros mínimos e nomeados. Evite `*args` e `kwargs` como se fossem fogo. - Um único propósito. Se a função faz duas coisas, divida. - Efeitos colaterais explícitos. Se ela escreve em arquivo, envia email ou altera estado global, isso deve estar documentado ou ser óbvio pelo nome. Eu costumo seguir uma regra simples de tamanho: se uma função passa de 30 linhas, olho com desconfiança. Não é lei, mas é indicativo de que talvez haja mais de uma responsabilidade ali.Tipos comuns que você vai encontrar
Funções puras não dependem de estado externo. Recebem dados, retornam resultado. São as mais previsíveis e as mais fáceis de testar. Funções com efeito colateral manipulam coisas fora do seu escopo. Ambas são válidas, mas você precisa saber qual está usando. No Python, temos também geradores, decoradores e funções assíncronas. Geradores economizam memória ao processar fluxos grandes. Decoradores injetam comportamento transversal, como log ou cache. Assíncronas permitem I/O concorrente sem multithreading. Nada disso é mágica, é sintaxe sobre o mesmo mecanismo.Erros que eu vejo todo mundo cometer
O primeiro é função gigante. Tipo 200 linhas, com cinco níveis de indentação, variáveis com nomes genéricos. Isso mata a manutenibilidade. O segundo é não tratar erros. Funções que assumem que tudo vai dar certo são armas carregadas. Se a entrada pode falhar, especifique o que acontece. O terceiro é ignorar a tipagem. Python permite passar qualquer coisa pra qualquer função. Anotações de tipo não obrigam nada, mas ajudam ferramentas de análise estática e leitores humanos a entenderem o contrato.Um exemplo prático, do tipo que funciona
def calcular_media_de_notas(alunos, peso_materia=1): if not alunos:
raise ValueError("Lista de alunos vazia") soma = 0
total = 0 for aluno in alunos:
nota = aluno.get("nota") materia = aluno.get("materia")
👉 Clique no botão abaixo para saber mais sobre o assunto!
if nota is None: continue
soma += nota * peso_materia.get(materia, 1) total += peso_materia.get(materia, 1)
if total == 0: raise ValueError("Nenhuma nota válida encontrada")
return soma / total
Esse exemplo mostra tratamento de cases limites, uso de pesos variáveis e feedback claro quando a entrada é inválida. Não é perfeito, mas é realista.