Trilha de aprendizado · Nível 9 · Tutorial 5

Especializar funções com functools.partial

Ao concluir, você será capaz de criar versões especializadas de funções existentes, preenchendo antecipadamente parte dos argumentos sem executar a operação original.

  • Nível: Intermediário
  • Duração: 15 min
  • 8 passos
Especializar funções com functools.partial

O que você vai percorrer

  1. Preparar uma chamada sem executar a função Crie uma versão especializada de uma função existente e diferencie sua criação da execução posterior. 2 min
  2. Completar os argumentos posicionais Veja como os argumentos guardados por partial ficam no início da chamada e complete os argumentos restantes na ordem correta. 2 min
  3. Predefinir e substituir argumentos nomeados Configure valores nomeados ao criar um partial e veja como uma chamada posterior pode substituí-los apenas naquele uso. 2 min
  4. Identificar conflitos de associação Diagnostique chamadas a objetos partial que atribuem dois valores ao mesmo parâmetro e escolha uma forma válida de corrigi-las. 2 min
  5. Entender o que fica armazenado Veja como referências a objetos mutáveis se comportam quando são antecipadas com functools.partial. 2 min
  6. Usar partial como chave de ordenação Especialize uma função de chave para que sorted forneça apenas o registro que ainda falta. 2 min
  7. Escolher entre partial e uma função própria Escolha a adaptação mais clara: somente preencher argumentos ou acrescentar comportamento. 2 min
  8. Aplicar e verificar uma especialização Crie e verifique localmente uma chave de ordenação especializada com functools.partial. 3 min

O que você vai aprender

  • Criar um objeto chamável com functools.partial a partir de uma função existente.
  • Combinar argumentos previamente definidos com os fornecidos na chamada posterior.
  • Prever substituições de argumentos nomeados e conflitos de associação de argumentos.
  • Escolher partial para preenchimento de argumentos e uma função própria quando houver lógica adicional.

Antes de começar

  • Passar funções como argumentos
  • Criar closures com estado e nonlocal
  • Receber argumentos variáveis com *args e **kwargs
  • Separar código em módulos e usar importações explícitas

Passo 1 de 8

Preparar uma chamada sem executar a função

Crie uma versão especializada de uma função existente e diferencie sua criação da execução posterior.

Especializar sem chamar

O que é aplicação parcial?

Aplicação parcial cria uma nova função chamável a partir de uma função existente, deixando parte dos argumentos já preparada. A função original não é executada nesse momento: você apenas monta uma versão especializada para usar depois.

Dois momentos distintos

Observe a separação entre preparar a chamada e executá-la.

Diagrama mostrando uma função original e um argumento fixo entrando em um objeto partial; em seguida, o objeto parcial sendo chamado em um momento posterior para executar a função.

Criar o objeto parcial prepara uma chamada futura; usar parênteses no objeto parcial é o que dispara a função original.

Criar e usar um partial

Uma saudação preparada

partial pertence à biblioteca padrão e é importado explicitamente de functools.

python
from functools import partial


def saudar(saudacao, nome):
    print(f"{saudacao}, {nome}!")


bom_dia = partial(saudar, "Bom dia")
print("Objeto parcial criado")

bom_dia("Ana")

Leia na ordem

Em partial(saudar, "Bom dia"), passamos a função saudar — sem parênteses — e o valor que ficará preparado. Portanto, essa linha cria bom_dia, mas não entra no corpo de saudar.

Só bom_dia("Ana") chama o objeto parcial e produz Bom dia, Ana!. Neste primeiro contato, foque na sequência: importar → criar → chamar depois.

Dica

Sinal de execução

Se a função original tem um print, uma alteração em dados ou outro efeito observável, esse efeito só acontece quando você chama o objeto parcial com parênteses.

Verifique o momento da execução

Quando aparece a mensagem?

Considere o código:

from functools import partial

def notificar(tipo, mensagem):
    print(f"[{tipo}] {mensagem}")

notificar_erro = partial(notificar, "ERRO")
notificar_erro("Arquivo ausente")

Em qual instrução o print dentro de notificar é executado?

Passo 2 de 8

Completar os argumentos posicionais

Veja como os argumentos guardados por partial ficam no início da chamada e complete os argumentos restantes na ordem correta.

O prefixo fica pronto

Argumentos guardados vêm primeiro

Ao criar um partial com argumentos posicionais, eles ficam armazenados como o início da futura chamada. Quando você chama o objeto parcial, os novos argumentos posicionais entram depois deles, na mesma ordem.

Como a chamada é montada

Imagine a função com três parâmetros. O primeiro já foi preenchido; os dois seguintes serão recebidos ao chamar a especialização.

Diagrama de três posições ordenadas: a primeira está preenchida previamente e as duas seguintes são completadas depois.

A chamada posterior completa o final da lista de argumentos; ela não substitui nem reorganiza o prefixo armazenado.

Reconstrua a chamada original

Uma função especializada

O objeto etiqueta_loja guarda somente o primeiro argumento posicional.

python
from functools import partial


def criar_etiqueta(origem, produto, lote):
    return f"{origem} | {produto} | lote {lote}"


etiqueta_loja = partial(criar_etiqueta, "Loja Centro")

print(etiqueta_loja("Caderno", 42))
print(etiqueta_loja("Caneta", 17))

Equivalência das chamadas

A primeira chamada é equivalente a criar_etiqueta("Loja Centro", "Caderno", 42). A segunda reutiliza o mesmo prefixo e equivale a criar_etiqueta("Loja Centro", "Caneta", 17).

O mesmo objeto parcial pode ser chamado várias vezes, desde que você forneça os argumentos que ainda faltam.

Dica

Leia da esquerda para a direita

Para prever uma chamada, escreva primeiro os argumentos que o partial guardou. Em seguida, acrescente os argumentos da chamada atual. Só então associe cada posição aos parâmetros da função.

Pratique a ordem dos argumentos

Complete a chamada equivalente

Considere:
gerar = partial(criar_etiqueta, "Norte")
gerar("Relatório", 3)

A chamada equivalente à função original é:
criar_etiqueta("Norte", ___, ___)

Passo 3 de 8

Predefinir e substituir argumentos nomeados

Configure valores nomeados ao criar um partial e veja como uma chamada posterior pode substituí-los apenas naquele uso.

Guardar uma configuração nomeada

Nomeie o valor que ficará pré-definido

Além de argumentos posicionais, partial pode receber argumentos nomeados. Eles ficam associados ao objeto chamável criado e serão usados quando você não informar outro valor para aquele mesmo nome na chamada.

Como os valores nomeados se combinam

O partial mantém a configuração cor="azul". A chamada posterior fornece a mensagem; se também fornecer cor, esse valor vale apenas para aquela execução.

Diagrama mostrando uma função com os parâmetros mensagem e cor, um objeto partial com cor azul armazenada e duas chamadas: uma usando azul e outra fornecendo laranja temporariamente.

O argumento nomeado armazenado completa a chamada, mas pode ser substituído por outro argumento nomeado na execução.

Criando uma especialização por nome

python
from functools import partial


def rotulo(mensagem, cor="cinza"):
    return f"{cor}: {mensagem}"


rotulo_azul = partial(rotulo, cor="azul")

print(rotulo_azul("Arquivo salvo"))
# azul: Arquivo salvo

Substituir sem reconfigurar

A chamada posterior tem prioridade

Se a chamada ao objeto parcial informar novamente um argumento nomeado já predefinido, o valor dessa chamada prevalece. Isso não altera a configuração guardada no objeto parcial: a substituição é pontual.

Uma substituição válida e uma chamada futura

python
print(rotulo_azul("Conexão perdida", cor="laranja"))
# laranja: Conexão perdida

print(rotulo_azul("Reconexão concluída"))
# azul: Reconexão concluída

Dica

Leitura prática

Leia rotulo_azul("...", cor="laranja") como: use a especialização azul, exceto nesta chamada, em que cor será laranja.

Preveja chamadas sucessivas

Resultado da substituição

Considerando o código anterior, complete o resultado de rotulo_azul("Conexão perdida", cor="laranja"):

____: Conexão perdida

Configuração preservada

Depois de chamar rotulo_azul("Conexão perdida", cor="laranja"), uma nova chamada a rotulo_azul("Reconexão concluída") usará cor="laranja".

Passo 4 de 8

Identificar conflitos de associação

Diagnostique chamadas a objetos partial que atribuem dois valores ao mesmo parâmetro e escolha uma forma válida de corrigi-las.

Quando dois valores chegam ao mesmo parâmetro

O partial não valida toda chamada futura

Um objeto criado com partial guarda argumentos, mas a associação final só acontece quando você o chama. Se a combinação dos valores guardados e dos novos valores tentar preencher o mesmo parâmetro duas vezes, Python lança TypeError.

Mapa da duplicidade

Observe como um valor nomeado guardado pode colidir com um valor posicional fornecido depois.

Diagrama de parâmetros evento e nivel. Um valor login ocupa evento; dois caminhos distintos, um nomeado e outro posicional, tentam ocupar nivel, indicando conflito.

Na chamada final, cada parâmetro da função precisa receber no máximo um valor.

Atenção

Criação não é chamada

partial(registrar, nivel="INFO") é válido. O erro só ocorre se uma chamada posterior também preencher nivel por posição, como em info_evento("login", "DEBUG").

Substituir por nome × duplicar por posição

Dois resultados diferentes

Execute cada chamada separadamente para comparar os casos.

python
from functools import partial


def registrar(evento, nivel="INFO"):
    return f"{nivel}: {evento}"

info_evento = partial(registrar, nivel="INFO")

print(info_evento("login", nivel="DEBUG"))  # válido: DEBUG: login
print(info_evento("login", "DEBUG"))        # TypeError

Por que apenas uma delas falha?

Na primeira chamada, nivel="DEBUG" substitui temporariamente o valor nomeado guardado. Na segunda, "DEBUG" ocupa o segundo parâmetro por posição, que corresponde a nivel; ao mesmo tempo, nivel="INFO" já foi guardado. Portanto, nivel recebe dois valores.

Exemplo

Outro tipo de colisão

evento_login = partial(registrar, "login") guarda evento por posição. A chamada evento_login(evento="logout") falha: evento recebe "login" pela posição guardada e "logout" pelo nome posterior.

Correção: use evento_login(nivel="DEBUG") se quiser apenas completar o outro parâmetro, ou chame registrar(evento="logout") se o evento precisa mudar.

Diagnostique e corrija

Relacione cada chamada ao diagnóstico

Considere info_evento = partial(registrar, nivel="INFO") e evento_login = partial(registrar, "login"), usando a função registrar(evento, nivel="INFO").

Toque em um item e depois no par correspondente.

Corrija uma chamada conflitante

A chamada info_evento("login", "DEBUG") gerou TypeError. Qual parâmetro recebeu dois valores? Escreva uma chamada válida que registre "login" no nível "DEBUG" e explique brevemente a mudança.

Escreva pelo menos 40 caracteres (0/40).

Passo 5 de 8

Entender o que fica armazenado

Veja como referências a objetos mutáveis se comportam quando são antecipadas com functools.partial.

Partial guarda a referência, não uma cópia

Ao criar um partial, os argumentos antecipados são guardados como referências aos objetos recebidos. Se um desses objetos for mutável, como uma lista, alterações feitas nele depois da criação poderão aparecer nas chamadas futuras.

Portanto, antecipar um argumento não cria uma fotografia imutável de seu conteúdo.

Diagrama mostrando um nome de variável e um objeto partial apontando para a mesma lista; após adicionar um item à lista, a chamada do partial consulta a lista alterada.

A lista é um único objeto compartilhado: tanto o nome externo quanto o partial apontam para ela.

Alterar a lista altera chamadas futuras

Experimente no seu editor

Crie um arquivo Python, execute-o e compare as duas saídas.

python
from functools import partial


def mostrar(itens, titulo):
    return f"{titulo}: {', '.join(itens)}"


frutas = ["maçã", "banana"]
mostrar_frutas = partial(mostrar, frutas)

print(mostrar_frutas("Antes"))

frutas.append("uva")
print(mostrar_frutas("Depois"))

Exemplo

Saída esperada

Antes: maçã, banana
Depois: maçã, banana, uva

append modifica o mesmo objeto de lista que foi armazenado por mostrar_frutas. Por isso, a segunda chamada enxerga "uva".

Atenção

Antecipar não congela

Não suponha que partial preserve o estado dos argumentos mutáveis no instante da criação. Ele reutiliza a referência que recebeu.

Reatribuir o nome não troca o objeto guardado

Reatribuição é diferente de mutação. Ao fazer frutas = ["pera"], o nome frutas passa a apontar para outra lista. Mas mostrar_frutas continua com a referência da lista original.

Compare com a reatribuição

python
from functools import partial


def mostrar(itens, titulo):
    return f"{titulo}: {', '.join(itens)}"


frutas = ["maçã", "banana"]
mostrar_frutas = partial(mostrar, frutas)

frutas = ["pera"]

print(mostrar_frutas("Guardado no partial"))
print(mostrar(frutas, "Nome frutas agora"))

Exemplo

Resultado

Guardado no partial: maçã, banana
Nome frutas agora: pera

A reatribuição não modifica a lista antiga nem substitui o argumento que o partial já guardou.

Preveja os dois cenários

Qual será a saída?

Considere:

from functools import partial

def total(valores, extra):
    return sum(valores) + extra

itens = [2, 3]
com_taxa = partial(total, itens)
itens.append(5)
itens = [100]

print(com_taxa(1))

Qual valor será exibido?

Passo 6 de 8

Usar partial como chave de ordenação

Especialize uma função de chave para que sorted forneça apenas o registro que ainda falta.

Uma chave configurável

De dois argumentos para um

Às vezes, a função que produz a chave de ordenação precisa de uma configuração e de um registro. Neste exemplo, chave_por_campo recebe o nome do campo e um dicionário. Porém, sorted(..., key=...) chamará sua chave com apenas um registro por vez.

Use partial para preencher antecipadamente o campo desejado e deixar o parâmetro registro disponível para sorted.

O argumento que fica em aberto

Diagrama mostrando o valor fixo campo igual a prioridade preenchendo o primeiro parâmetro de chave_por_campo, enquanto cada registro fornecido por sorted ocupa o parâmetro registro; o resultado é uma chave de comparação.

partial fixa a configuração; sorted entrega cada registro à função especializada.

Especialize e passe a função

Ordenar por prioridade

Crie a especialização sem usar parênteses depois de por_prioridade ao passá-la para key.

python
from functools import partial


def chave_por_campo(campo, registro):
    return registro[campo]


chamados = [
    {"titulo": "Revisar relatório", "prioridade": 2},
    {"titulo": "Corrigir erro", "prioridade": 1},
    {"titulo": "Atualizar guia", "prioridade": 3},
]

por_prioridade = partial(chave_por_campo, "prioridade")
ordenados = sorted(chamados, key=por_prioridade)

print([chamado["titulo"] for chamado in ordenados])
# ['Corrigir erro', 'Revisar relatório', 'Atualizar guia']

Dica

Sem chamada antecipada

key=por_prioridade passa o objeto chamável para sorted. Durante a ordenação, sorted chama algo equivalente a por_prioridade(registro) para cada dicionário.

Complete a chave e preveja o resultado

Qual nome falta?

Complete a criação para ordenar os produtos pelo campo "nome":

por_nome = _____(chave_por_campo, "nome")

Siga uma chamada de sorted

Considere produtos = [{"nome": "Caio"}, {"nome": "Ana"}, {"nome": "Bia"}] e por_nome = partial(chave_por_campo, "nome").

Qual será a ordem dos nomes em sorted(produtos, key=por_nome)? Explique qual argumento sorted fornece a por_nome.

Escreva pelo menos 50 caracteres (0/50).

Passo 7 de 8

Escolher entre partial e uma função própria

Escolha a adaptação mais clara: somente preencher argumentos ou acrescentar comportamento.

A pergunta principal: há comportamento novo?

Preencher não é transformar

Use partial quando a especialização apenas fixa argumentos de uma função que já faz exatamente o trabalho necessário. O objeto resultante continua esperando somente os argumentos que faltaram.

Crie uma função própria quando a adaptação precisar fazer algo além de encaminhar argumentos: validar uma entrada, transformar um valor, tomar uma decisão ou combinar mais de uma operação.

Dois tipos de adaptação

Compare o caminho direto de partial com uma função que adiciona uma etapa antes de chamar a operação-base.

Diagrama comparando uma função-base ligada diretamente a um objeto parcial com argumentos fixos e uma função própria que valida e transforma uma entrada antes de chamar a função-base.

partial fixa argumentos; uma função própria comporta lógica adicional.

Comparando soluções

Exemplo

Quando partial comunica a intenção

from functools import partial


def formatar_preco(valor, moeda):
    return f"{moeda} {valor:.2f}"


formatar_em_real = partial(formatar_preco, moeda="R$")

print(formatar_em_real(19.9))  # R$ 19.90

formatar_em_real apenas fornece moeda="R$". Não há regra nova: partial deixa isso explícito.

Exemplo

Quando uma função própria comunica melhor

def formatar_preco(valor, moeda):
    return f"{moeda} {valor:.2f}"


def formatar_preco_promocional(valor):
    if valor < 0:
        raise ValueError("O preço não pode ser negativo")

    valor_com_desconto = valor * 0.9
    return formatar_preco(valor_com_desconto, moeda="R$")

Aqui há validação e cálculo de desconto. Uma função nomeada expõe esse comportamento e dá um nome útil à regra de negócio.

Dica

Critério de legibilidade

Não escolha pelo número de linhas. Escolha pelo contrato: se a nova chamada só completa argumentos, partial costuma ser direto. Se ela adiciona uma regra própria, dê um nome a essa regra em uma função.

Classifique cada cenário

partial ou função própria?

Associe cada cenário à solução mais clara.

Toque em um item e depois no par correspondente.

Decida pela responsabilidade

Escolha a adaptação

Uma função deve receber uma pontuação, limitar qualquer valor acima de 100 a 100 e então chamar registrar_resultado(pontuacao, categoria="simulado"). Qual opção é mais clara?

Passo 8 de 8

Aplicar e verificar uma especialização

Crie e verifique localmente uma chave de ordenação especializada com functools.partial.

Da função-base à chave de ordenação

Uma especialização reutilizável

Nesta aplicação, a função-base recebe primeiro o campo e depois o registro. Ao criar partial(chave, "nome"), o campo fica preenchido e sobra apenas o registro — exatamente o argumento que sorted(..., key=...) fornece para cada item.

Argumentos nomeados armazenados podem ser substituídos em uma chamada isolada. Já misturar um valor posicional já armazenado com outro valor nomeado para o mesmo parâmetro produz TypeError na hora da chamada.

Mapa dos argumentos

Diagrama mostrando o valor nome preenchendo antecipadamente o primeiro parâmetro de uma função chave, enquanto registros sucessivos preenchem o parâmetro restante durante a ordenação.

O objeto parcial guarda o campo como prefixo posicional; sorted entrega um registro por vez ao parâmetro que sobrou.

Prática local: ordenar e verificar

Execute no seu computador

Crie um arquivo chamado ordenar_registros.py, copie o script completo abaixo e execute python ordenar_registros.py no terminal. Os asserts não exibem nada quando passam; ao final, o script imprime a lista ordenada e confirma que o conflito foi observado.

ordenar_registros.py

python
from functools import partial


def chave(campo, registro, maiusculas=False):
    valor = registro[campo]
    return valor.upper() if maiusculas else valor


registros = [
    {"nome": "Carlos", "cidade": "Salvador"},
    {"nome": "Ana", "cidade": "Recife"},
    {"nome": "Beatriz", "cidade": "Manaus"},
]

# "nome" fica armazenado como o primeiro argumento posicional.
por_nome = partial(chave, "nome")
ordenados = sorted(registros, key=por_nome)

assert [registro["nome"] for registro in ordenados] == [
    "Ana",
    "Beatriz",
    "Carlos",
]

# O argumento nomeado posterior substitui apenas nesta chamada.
por_nome_normalizado = partial(chave, "nome", maiusculas=False)
assert por_nome_normalizado(registros[1], maiusculas=True) == "ANA"
assert por_nome_normalizado(registros[1]) == "Ana"

# O campo já foi fornecido por posição em por_nome.
# Fornecê-lo novamente por nome causa conflito na chamada.
conflito_observado = False
try:
    por_nome(registros[0], campo="cidade")
except TypeError:
    conflito_observado = True

assert conflito_observado

print([registro["nome"] for registro in ordenados])
print("Ordenação verificada e conflito TypeError observado.")

Revisão e aplicação final

Registre sua verificação

Depois de executar o script, relate: a ordem impressa, o que aconteceu com a substituição de maiusculas, por que houve TypeError e por que partial é adequado aqui. Diga também um caso em que você escreveria uma função própria.

Escreva pelo menos 120 caracteres (0/120).

Resumo

Síntese do tutorial

  • partial(funcao, ...) cria um objeto chamável e não executa a função-base durante sua criação.
  • Argumentos posicionais armazenados entram antes dos argumentos da chamada posterior; argumentos nomeados posteriores podem substituir nomes armazenados.
  • Uma chamada que associa o mesmo parâmetro mais de uma vez falha com TypeError, mesmo que o objeto parcial tenha sido criado sem erro.
  • partial guarda referências aos objetos recebidos: alterar um objeto mutável pode afetar chamadas futuras; reatribuir apenas o nome externo não troca a referência guardada.
  • Use partial quando a adaptação for só preencher argumentos. Prefira uma função própria quando precisar validar, transformar valores, decidir caminhos ou combinar operações.

Especialização verificada

Parabéns! Você concluiu: Especializar funções com functools.partial

Você integrou functools.partial em uma ordenação, verificou resultados e reconheceu uma chamada conflitante. Agora você sabe quando especializar uma função existente e quando criar uma adaptação com lógica própria.

20 XP

Baixe o Aplicativo agora para ter acesso a + de 5000 cursos gratuitos, exercícios, certificado e muito conteúdo sem pagar nada!

  • Cursos online 100% gratuitos do início ao fim

    Milhares de cursos online em vídeo, ebooks e áudiobooks.

  • Mais de 60 mil exercícios gratuitos

    Para testar seus conhecimentos no decorrer dos cursos online

  • Certificado Digital gratuito válido em todo o Brasil

    Gerado diretamente na galeria de fotos do seu celular e enviado ao seu e-mail

Aplicativo Cursa na tela de ebook, na tela de curso em vídeo e na tela de exercícios do curso, mais o certificado de conclusão de curso