O functools.partial no Python permite criar novas funções a partir de funções existentes, fixando antecipadamente alguns argumentos. O recurso é simples, mas extremamente útil quando você precisa adaptar callbacks, reduzir repetição, preparar funções para APIs que exigem uma assinatura específica ou deixar pipelines mais legíveis.
Neste guia, você vai entender como partial() funciona, quando ele melhora o código, quais armadilhas evitar e como combiná-lo com funções nomeadas, métodos, decorators, callbacks e testes. O conteúdo complementa nossos artigos sobre funções em Python, funções lambda, decorators, args e kwargs e type hints.
O que é functools.partial
partial() recebe uma função e alguns argumentos. O resultado é um novo objeto chamável que reutiliza a função original com esses valores já preenchidos.
from functools import partial
def potencia(base, expoente):
return base ** expoente
quadrado = partial(potencia, expoente=2)
print(quadrado(5)) # 25A função original continua intacta. O objeto quadrado apenas guarda que o argumento expoente deve valer 2. Quando você chama quadrado(5), internamente o Python executa potencia(5, expoente=2).
Por que usar partial em vez de repetir argumentos
Imagine uma aplicação que converte vários valores para reais usando sempre a mesma taxa:
def converter(valor, taxa, arredondar=True):
resultado = valor * taxa
return round(resultado, 2) if arredondar else resultado
converter_para_real = partial(converter, taxa=5.42)
print(converter_para_real(10))
print(converter_para_real(25))Sem partial, você repetiria taxa=5.42 em toda chamada ou criaria uma função wrapper manual. A versão parcial deixa claro que existe uma operação especializada.
Argumentos posicionais
Os argumentos posicionais fornecidos ao criar o partial são inseridos antes dos argumentos enviados posteriormente.
def juntar(prefixo, texto, sufixo):
return f"{prefixo}{texto}{sufixo}"
com_colchetes = partial(juntar, "[")
print(com_colchetes("Python", "]"))O resultado será [Python]. Esse comportamento exige atenção à ordem da assinatura original.
Argumentos nomeados
Argumentos nomeados são frequentemente mais legíveis:
import json
from functools import partial
json_legivel = partial(
json.dumps,
ensure_ascii=False,
indent=2,
sort_keys=True,
)
print(json_legivel({"curso": "Python", "ativo": True}))Esse padrão cria um serializador configurado uma única vez. A documentação oficial de functools.partial detalha como os argumentos são armazenados e combinados.
Sobrescrever argumentos
Argumentos nomeados definidos no partial podem ser substituídos em uma chamada posterior:
json_compacto = partial(json.dumps, ensure_ascii=False, indent=2)
print(json_compacto({"a": 1}, indent=None))O valor mais recente de indent prevalece. Use essa flexibilidade quando fizer sentido, mas evite criar objetos parciais cuja configuração seja constantemente anulada.
Callbacks com assinatura compatível
Muitas bibliotecas chamam callbacks com uma assinatura fixa. partial ajuda a anexar contexto sem usar variáveis globais.
def registrar_evento(categoria, mensagem):
print(f"[{categoria}] {mensagem}")
registrar_erro = partial(registrar_evento, "ERRO")
registrar_info = partial(registrar_evento, "INFO")
registrar_erro("Arquivo não encontrado")
registrar_info("Processamento concluído")Em interfaces gráficas, filas, schedulers e frameworks web, esse padrão torna callbacks pequenos e previsíveis.
Uso com map
partial pode preparar uma função para map():
def aplicar_desconto(valor, percentual):
return valor * (1 - percentual)
com_desconto = partial(aplicar_desconto, percentual=0.10)
precos = [100, 250, 80]
resultado = list(map(com_desconto, precos))
print(resultado)O resultado é uma lista com desconto de 10% aplicado a cada valor.
Uso com sorted e key
Uma função parcial também pode ser usada como chave de ordenação:
def distancia(alvo, valor):
return abs(valor - alvo)
mais_perto_de_10 = partial(distancia, 10)
valores = [1, 20, 8, 13, 5]
print(sorted(valores, key=mais_perto_de_10))O código deixa explícito que a ordenação considera a distância até 10.
partial versus lambda
Uma lambda poderia resolver muitos dos mesmos casos:
quadrado_lambda = lambda base: potencia(base, expoente=2)
quadrado_partial = partial(potencia, expoente=2)partial tende a ser melhor quando a intenção é apenas fixar argumentos. Lambda é mais adequada quando existe transformação adicional, condição ou composição de expressões.
Vantagens sobre wrappers manuais
Um wrapper manual permite documentação e lógica adicional:
def quadrado(base):
"""Retorna o quadrado de um número."""
return potencia(base, 2)Esse formato é preferível quando a função especializada faz parte da API pública, precisa de docstring própria ou contém validação. Use partial para adaptações diretas e locais.
Inspecionar o objeto partial
Objetos parciais expõem atributos úteis:
quadrado.func
quadrado.args
quadrado.keywordsfunc: função original;args: argumentos posicionais fixados;keywords: argumentos nomeados fixados.
Esses atributos ajudam em testes, depuração e ferramentas de introspecção.
Nome e documentação
Um objeto partial não recebe automaticamente o mesmo __name__ e a mesma docstring da função original. Para APIs públicas, você pode usar functools.update_wrapper().
from functools import partial, update_wrapper
quadrado = partial(potencia, expoente=2)
update_wrapper(quadrado, potencia)
Mesmo assim, o nome herdado continuará representando a função original, não necessariamente a especialização. Em código público, um wrapper nomeado pode comunicar melhor a intenção.
partialmethod em classes
Dentro de classes, existe functools.partialmethod, criado para funcionar corretamente com o protocolo de descriptors.
from functools import partialmethod
class Publicador:
def enviar(self, mensagem, nivel):
print(nivel, mensagem)
informar = partialmethod(enviar, nivel="INFO")
alertar = partialmethod(enviar, nivel="ALERTA")
p = Publicador()
p.informar("Servidor iniciado")A documentação oficial de partialmethod explica o comportamento com métodos e descriptors.
Objetos mutáveis
Os argumentos são guardados por referência. Se você fixar uma lista ou dicionário e depois modificá-lo, as chamadas futuras verão o conteúdo atualizado.
config = {"modo": "teste"}
def executar(nome, config):
return nome, config
executar_configurado = partial(executar, config=config)
config["modo"] = "produção"
print(executar_configurado("backup"))Esse comportamento pode ser intencional, mas frequentemente gera surpresas. Prefira objetos imutáveis ou copie a configuração no momento adequado.
Assinaturas e ferramentas
inspect.signature() normalmente consegue mostrar uma assinatura adaptada para objetos partial, o que melhora integração com IDEs e validação.
from inspect import signature
print(signature(quadrado))Ainda assim, frameworks que dependem de metadados específicos podem exigir wrappers explícitos.
Type hints
Ferramentas de tipagem podem inferir parte do comportamento, mas especializações complexas nem sempre ficam claras. Quando a função parcial é importante, atribua uma anotação:
from collections.abc import Callable
quadrado_tipado: Callable[[float], float] = partial(
potencia,
expoente=2,
)Isso melhora autocomplete e análise estática.
Testar funções parciais
Teste o comportamento, não apenas a existência do objeto:
def test_quadrado():
assert quadrado(4) == 16
assert quadrado(-3) == 9Quando os argumentos fixados fazem parte da regra, você também pode verificar args e keywords.
Serialização
Objetos parciais podem ser serializados em alguns contextos quando a função e os argumentos também são serializáveis. Não dependa disso para executar dados não confiáveis. Formatos baseados em pickle podem executar código durante a desserialização.
Desempenho
O custo adicional de um partial costuma ser pequeno e raramente deve orientar a decisão. Priorize clareza e meça com timeit quando estiver em um trecho realmente crítico.
Erros frequentes
- Fixar argumentos na ordem errada.
- Usar partial quando um wrapper documentado seria mais claro.
- Armazenar listas e dicionários mutáveis sem perceber.
- Esquecer que keywords podem ser sobrescritas.
- Usar partialmethod de forma intercambiável com partial em classes.
- Presumir que nome e docstring serão preservados.
- Criar especializações demais e dificultar a leitura.
Boas práticas
- Use nomes que revelem a especialização.
- Prefira keywords para configurações importantes.
- Mantenha o objeto parcial próximo de onde é usado.
- Evite argumentos mutáveis compartilhados.
- Use wrappers para APIs públicas e validações.
- Documente callbacks que carregam contexto.
- Teste a assinatura esperada pela biblioteca consumidora.
Conclusão
O functools.partial no Python é uma ferramenta prática para especializar funções sem duplicar lógica. Ele reduz repetição, facilita callbacks, adapta assinaturas e deixa configurações recorrentes explícitas.
Use partial quando a transformação consiste principalmente em fixar argumentos. Quando você precisar de documentação própria, validação, tratamento de erros ou lógica adicional, uma função wrapper tradicional será mais clara. Com essa distinção, o recurso melhora a legibilidade sem esconder o fluxo do programa.





