Um contrato que se consegue explicar
Uma função deve ter uma responsabilidade reconhecível. Distingue devolver um resultado de o imprimir: return entrega um valor ao chamador, enquanto print escreve uma representação. Uma função sem return explícito devolve None. Anotações de tipos documentam intenções, mas não validam entradas automaticamente.
Valores por defeito mutáveis
Os argumentos por defeito são avaliados quando a função é definida. Uma lista usada como valor por defeito pode acumular alterações entre chamadas. Usa None e cria a lista dentro da função quando pretendes uma coleção nova por chamada.
Aplicação guiada no trabalho
Numa função collect, usar items=None permite criar uma coleção quando o chamador a omite. Usa if items is None para não substituir uma lista vazia que o chamador pretende preencher. O padrão items = items or [] falha esse contrato porque trata a lista vazia como ausência. Se a função deve ser pura e devolver uma coleção nova, documenta esse contrato diferente e adapta os chamadores. Mantém validação separada de efeitos como envio de email ou escrita de ficheiros, para poderes reutilizar a regra. Anotações de tipos ajudam a comunicar entradas e resultados, mas numa função normal não convertem nem rejeitam dados externos automaticamente. Uma camada de validação tem de implementar esse comportamento explicitamente.
Preservar a causa de uma falha
Uma função pode converter uma exceção técnica numa exceção do domínio sem perder a relação causal. Usa raise BatchImportError("invalid input") from exc quando a causa original deve ficar associada. Não devolvas uma string de erro no lugar de um resultado normal se o chamador espera uma exceção. Se a mensagem tiver dados sensíveis, cria um resumo adequado; preservar encadeamento e decidir o que se mostra ao utilizador são responsabilidades distintas.
Captura de valores e parâmetros explícitos
Uma função criada num ciclo pode consultar a variável externa só quando for chamada, encontrando o último valor do ciclo. Para callbacks por ID, captura o valor durante a criação ou passa-o explicitamente; mudar lambda para def não resolve por si só. Numa assinatura, parâmetros depois de * são keyword-only. Exigir dry_run por nome reduz confusão na chamada, mas não implementa a ausência de efeitos: o corpo da função e os testes têm de cumprir esse contrato.
def collect(item, items=None):
if items is None:
items = []
items.append(item)
return itemsUma função de validação deve devolver os erros encontrados sem enviar emails ou alterar ficheiros. Separar esses efeitos permite reutilizar e testar a mesma regra numa API e num processo batch.
Armadilhas comuns
Usar valores por defeito mutáveis; substituir listas vazias fornecidas; confiar em anotações como validação.
Tópicos relacionados: Erros e ficheiros com contexto · Precisão, dados externos e tempo
Explicita entradas e efeitos; usa None quando um argumento opcional precisa de um novo objeto mutável.
Referência: Python 3.14: Control flow and functions · Python 3.14; DR Python 2026.3