
Passo 1 de 6
Separar configuração e argumentos da chamada
Entenda quais valores configuram o decorador e quais valores chegam à função decorada quando ela é chamada.
Trilha de aprendizado · Nível 9 · Tutorial 7
Ao concluir, você será capaz de configurar comportamentos adicionados por decoradores, separando os parâmetros da decoração dos argumentos usados para chamar a função decorada.
Separar configuração e argumentos da chamada
Entenda quais valores configuram o decorador e quais valores chegam à função decorada quando ela é chamada. 2 min
Montar a fábrica, o decorador e o wrapper
Organize um decorador parametrizado em três camadas: configurar, receber a função original e envolver cada chamada. 3 min
Acompanhar a definição e as chamadas
Entenda em que momento a fábrica, o decorador e o wrapper atuam em uma função decorada com configuração. 3 min
Validar e conservar a configuração
Valide o rótulo no momento em que a fábrica é chamada e use a versão normalizada nas chamadas posteriores. 3 min
Isolar configuração e estado por função
Escolha onde criar um contador para que ele tenha o tempo de vida desejado: por função decorada, compartilhado por um decorador reutilizado ou reiniciado a cada chamada. 4 min
Aplicar um decorador parametrizado completo
Integre validação, configuração capturada, preservação de metadados e contagem independente por função em um decorador parametrizado. 4 min

Passo 1 de 6
Entenda quais valores configuram o decorador e quais valores chegam à função decorada quando ela é chamada.
Você já usou @decorador para adicionar um comportamento a uma função. Agora, queremos configurar esse comportamento.
No caso condutor, registrar_chamadas exibirá uma mensagem antes de executar a função. O valor rotulo define qual mensagem de registro será usada.
A expressão @registrar_chamadas(rotulo="CÁLCULO") chama uma função externa para produzir o decorador. Ela não chama a função que aparece logo abaixo.
Observe que a configuração da decoração e os dados de uma chamada posterior percorrem caminhos diferentes.

O rótulo configura o comportamento acrescentado; os argumentos pertencem à execução da função.
Exemplo
@registrar_chamadas(rotulo="CÁLCULO")
def somar(valor_a, valor_b, *, arredondar=False):
total = valor_a + valor_b
return round(total) if arredondar else total
resultado = somar(2.4, 3.2, arredondar=True)Neste exemplo:
rotulo="CÁLCULO" configura o comportamento de registro.2.4, 3.2 e arredondar=True são argumentos da chamada de somar.registrar_chamadas pertencem à configuração do decorador. Eles não executam somar.Dica
Para classificar um valor, pergunte: ele aparece na expressão após @ ou na chamada feita pelo código, como somar(...)? No primeiro caso, configura a decoração; no segundo, é destinado à função decorada.
Considere:
@registrar_chamadas(rotulo="PEDIDO")
def calcular_total(preco, quantidade, desconto=0):
return preco * quantidade - desconto
calcular_total(25, 3, desconto=5)Qual alternativa separa corretamente configuração e argumentos da chamada?

Passo 2 de 6
Organize um decorador parametrizado em três camadas: configurar, receber a função original e envolver cada chamada.
Em um decorador parametrizado, uma função externa atua como fábrica: ela recebe a configuração, como rotulo. Dentro dela, o decorador recebe a função original. Por fim, o wrapper usa o rótulo capturado, adiciona o comportamento desejado e encaminha a chamada.
A fábrica retorna o decorador; o decorador retorna o wrapper. O wrapper é o objeto que ficará associado ao nome da função decorada.

O rótulo pertence ao ambiente da fábrica e pode ser consultado pelo wrapper interno.
O rotulo é recebido uma única vez pela fábrica e permanece disponível para o wrapper como variável capturada. @wraps(funcao) fica imediatamente antes do wrapper, usando a função original como referência.
from functools import wraps
def registrar_chamadas(rotulo):
def decorador(funcao):
@wraps(funcao)
def wrapper(*args, **kwargs):
print(f"[{rotulo}] chamando {funcao.__name__}")
return funcao(*args, **kwargs)
return wrapper
return decorador
@registrar_chamadas(rotulo="RELATÓRIO")
def somar(a, b):
"""Devolve a soma de dois números."""
return a + b
resultado = somar(2, 3)
print(resultado)Exemplo
registrar_chamadas("RELATÓRIO") produz um decorador configurado.
Esse decorador recebe somar e produz wrapper.
Assim, a decoração pode ser entendida como:
somar = registrar_chamadas("RELATÓRIO")(somar)Os return da fábrica e do decorador devolvem funções. O return funcao(*args, **kwargs) do wrapper devolve o resultado da chamada encaminhada.
Dica
Mesmo que o wrapper acrescente um registro, ele deve encaminhar *args e **kwargs e devolver o resultado recebido. Com @wraps(funcao), metadados como nome e docstring também continuam associados à função decorada.
Complete o parâmetro da fábrica:
def registrar_chamadas(____):
...Complete o retorno do decorador:
def decorador(funcao):
def wrapper(*args, **kwargs):
return funcao(*args, **kwargs)
return ____Complete a linha que preserva os metadados:
def decorador(funcao):
____
def wrapper(*args, **kwargs):
return funcao(*args, **kwargs)
Passo 3 de 6
Entenda em que momento a fábrica, o decorador e o wrapper atuam em uma função decorada com configuração.
Em uma definição como @registrar_chamadas(rotulo="RELATÓRIO"), os parênteses chamam a fábrica, não a função que será decorada.
A fábrica devolve um decorador. Em seguida, esse decorador recebe a função recém-criada e devolve o wrapper, que passa a ficar associado ao nome gerar_relatorio.
@registrar_chamadas(rotulo="RELATÓRIO")
def gerar_relatorio(mes):
print(f"Relatório de {mes}")
# Equivale a:
def gerar_relatorio(mes):
print(f"Relatório de {mes}")
decorador = registrar_chamadas(rotulo="RELATÓRIO")
gerar_relatorio = decorador(gerar_relatorio)
A fábrica e o decorador atuam na definição; o wrapper atua nas chamadas posteriores.
def registrar_chamadas(rotulo):
print("1. fábrica")
def decorador(funcao):
print("2. decorador")
def wrapper(*args, **kwargs):
print(f"3. wrapper: {rotulo}")
return funcao(*args, **kwargs)
return wrapper
return decorador
@registrar_chamadas(rotulo="RELATÓRIO")
def gerar_relatorio(mes):
print(f"4. função original: {mes}")
print("5. após a definição")
gerar_relatorio("janeiro")
gerar_relatorio("fevereiro")Ao executar a definição decorada, aparecem 1. fábrica e 2. decorador. O wrapper é criado e associado a gerar_relatorio, mas o corpo da função original ainda não roda.
Depois aparece 5. após a definição. Em cada chamada, o wrapper imprime a mensagem 3 e então chama a função original, que imprime a mensagem 4. A fábrica não é chamada outra vez automaticamente.
Dica
Separe mentalmente dois períodos: definição cria e aplica a decoração; chamada executa o wrapper e, neste caso, a função original. Duas chamadas produzem duas execuções do wrapper e da função original.
Considere o código anterior. Coloque estes eventos na ordem em que ocorrem até o término da primeira chamada gerar_relatorio("janeiro").
Depois que a definição decorada terminou, gerar_relatorio("janeiro") e gerar_relatorio("fevereiro") são executadas. Qual evento ocorre duas vezes?

Passo 4 de 6
Valide o rótulo no momento em que a fábrica é chamada e use a versão normalizada nas chamadas posteriores.
Para este exemplo, rotulo deve ser uma string que continue não vazia após remover os espaços das extremidades.
A fábrica é o lugar certo para aplicar esse contrato: ela recebe a configuração antes de retornar o decorador. Assim, um tipo incompatível gera TypeError; uma string que vira vazia após strip() gera ValueError.
Com @registrar_chamadas(rotulo=" "), a falha ocorre enquanto a definição decorada está sendo executada. O wrapper e a função original ainda não foram chamados.
A configuração atravessa a validação uma única vez, antes de a função decorada ficar disponível.

A validação pertence à fábrica; as chamadas posteriores consultam o rótulo que ela aprovou e normalizou.
A variável rotulo_normalizado é criada após as validações. O wrapper a consulta quando cada chamada acontece.
from functools import wraps
def registrar_chamadas(rotulo):
if not isinstance(rotulo, str):
raise TypeError("rotulo deve ser uma string")
rotulo_normalizado = rotulo.strip()
if not rotulo_normalizado:
raise ValueError("rotulo não pode ficar vazio")
def decorador(funcao):
@wraps(funcao)
def wrapper(*args, **kwargs):
print(f"[{rotulo_normalizado}] chamando {funcao.__name__}")
return funcao(*args, **kwargs)
return wrapper
return decorador
@registrar_chamadas(" cálculo ")
def somar(a, b):
return a + b
print(somar(2, 3))
# [cálculo] chamando somar
# 5Atenção
Neste caso, o rótulo normalizado é uma string e o wrapper apenas o consulta; isso conserva a configuração textual escolhida. Mas uma closure captura vínculos, não faz cópias automáticas de objetos mutáveis. Não conclua que qualquer configuração mutável ficaria isolada só por estar em uma closure.
Exemplo
Este código falha ao tentar criar a função decorada:
<code>@registrar_chamadas(" ")
def enviar():
print("enviando")</code>
Após strip(), o rótulo é vazio. A fábrica lança ValueError, portanto enviar não é disponibilizada e seu corpo não executa.
Relacione cada valor passado para rotulo ao resultado da fábrica.
Toque em um item e depois no par correspondente.
Por que @registrar_chamadas(" ") pode gerar um erro mesmo que ninguém chame a função decorada?
Escreva pelo menos 40 caracteres (0/40).

Passo 5 de 6
Escolha onde criar um contador para que ele tenha o tempo de vida desejado: por função decorada, compartilhado por um decorador reutilizado ou reiniciado a cada chamada.
Cada chamada a registrar_chamadas(...) cria um novo ambiente de configuração. Assim, chamadas distintas podem ter rótulos distintos.
Também é possível reutilizar o mesmo decorador retornado pela fábrica. Nesse caso, o rótulo capturado pode ser compartilhado intencionalmente. Para que cada função ainda tenha sua própria contagem, crie chamadas = 0 dentro de decorador, antes de definir o wrapper.
O wrapper consulta o rótulo e atualiza somente o contador associado à função que ele envolveu.
O diagrama mostra que um decorador reutilizado pode fornecer o mesmo rótulo a dois wrappers, sem obrigá-los a compartilhar o contador.

O rótulo pertence ao ambiente da fábrica; cada contador pertence à execução individual do decorador sobre uma função.
Observe onde chamadas é inicializado: dentro de decorador.
from functools import wraps
def registrar_chamadas(rotulo):
def decorador(funcao):
chamadas = 0
@wraps(funcao)
def wrapper(*args, **kwargs):
nonlocal chamadas
chamadas += 1
print(f"[{rotulo}] chamada {chamadas}: {funcao.__name__}")
return funcao(*args, **kwargs)
return wrapper
return decorador
registrar_tarefa = registrar_chamadas("TAREFA")
@registrar_tarefa
def enviar(nome):
return f"Envio de {nome} iniciado"
@registrar_tarefa
def cancelar(nome):
return f"Envio de {nome} cancelado"
print(enviar("relatorio.pdf"))
print(cancelar("rascunho.txt"))
print(enviar("dados.csv"))
print(cancelar("imagem.png"))
# [TAREFA] chamada 1: enviar
# [TAREFA] chamada 1: cancelar
# [TAREFA] chamada 2: enviar
# [TAREFA] chamada 2: cancelarO contador só persiste enquanto o ambiente que o contém continuar acessível:
1 e descartado logo depois.Estes trechos destacam os efeitos de colocar o contador em outro escopo.
# 1. Contador na fábrica: compartilhado se o mesmo decorador for reutilizado.
def registrar_compartilhado(rotulo):
chamadas = 0
def decorador(funcao):
def wrapper(*args, **kwargs):
nonlocal chamadas
chamadas += 1
return funcao(*args, **kwargs)
return wrapper
return decorador
# 2. Contador no wrapper: reinicia em toda chamada.
def registrar_reiniciado(rotulo):
def decorador(funcao):
def wrapper(*args, **kwargs):
chamadas = 0
chamadas += 1 # sempre resulta em 1
return funcao(*args, **kwargs)
return wrapper
return decoradorAssocie cada local de inicialização ao comportamento do contador quando um decorador retornado é reutilizado em duas funções.
Toque em um item e depois no par correspondente.
Você reutiliza registrar_tarefa para decorar enviar e cancelar, mas quer que ambas comecem em chamada 1 e avancem de forma independente. Em qual camada você inicializa chamadas? Justifique usando o momento em que essa camada é executada.
Escreva pelo menos 80 caracteres (0/80).

Passo 6 de 6
Integre validação, configuração capturada, preservação de metadados e contagem independente por função em um decorador parametrizado.
Neste exemplo, a fábrica recebe e valida o rótulo. O decorador recebe cada função. Dentro dele, o contador nasce uma vez para aquela função decorada; o wrapper o atualiza a cada chamada, registra a mensagem e devolve o resultado original.
Assim, somar e saudar terão rótulos e contagens próprios, mesmo quando suas chamadas forem intercaladas.
O diagrama mostra os ambientes criados para duas aplicações do mesmo padrão de decorador.

O rótulo é configurado na fábrica; o contador é criado no decorador para pertencer a uma única função decorada.
Para que cada função decorada tenha sua própria contagem persistente, crie quantidade = 0 dentro da função ____.
Crie um arquivo chamado decorador_completo.py, copie o código e execute python decorador_completo.py no terminal, na pasta do arquivo. Não há dependências externas.
Antes de executar, observe: um rótulo inválido falharia ao aplicar @registrar_chamadas(...), isto é, durante a definição decorada. Já 2 e 3, por exemplo, são argumentos que chegam a somar somente quando ela é chamada.
Fábrica, decorador e wrapper integrados.
from functools import wraps
def registrar_chamadas(rotulo):
if not isinstance(rotulo, str):
raise TypeError("rotulo deve ser uma string")
rotulo = rotulo.strip()
if not rotulo:
raise ValueError("rotulo não pode ficar vazio")
def decorar(funcao):
quantidade = 0
@wraps(funcao)
def wrapper(*args, **kwargs):
nonlocal quantidade
quantidade += 1
print(f"[{rotulo}] chamada {quantidade}")
return funcao(*args, **kwargs)
return wrapper
return decorar
@registrar_chamadas("Cálculo")
def somar(a, b):
return a + b
@registrar_chamadas("Saudação")
def saudar(nome):
return f"Olá, {nome}!"
print(somar(2, 3))
print(saudar("Lia"))
print(somar(10, 5))
print(saudar("Ravi"))
print(somar.__name__)
print(saudar.__name__)Exemplo
[Cálculo] chamada 1
5
[Saudação] chamada 1
Olá, Lia!
[Cálculo] chamada 2
15
[Saudação] chamada 2
Olá, Ravi!
somar
saudar
As mensagens mostram contagens independentes. As duas últimas linhas confirmam que @wraps(funcao) preservou os nomes das funções.
Execute o script. Quais rótulos e números de chamada você observou nas chamadas intercaladas? Inclua também os resultados devolvidos por somar e saudar.
Escreva pelo menos 80 caracteres (0/80).
Resumo
Use esta sequência ao criar outro decorador parametrizado:
quantidade = 0.@wraps(funcao) fica no wrapper interno para preservar metadados da função original.somar(2, 3) pertencem à chamada posterior; eles não reconfiguram o rótulo definido em @registrar_chamadas("Cálculo").Por que somar e saudar começam ambas na chamada 1, apesar de usarem o mesmo padrão registrar_chamadas?
Parabéns! Você concluiu: Criar decoradores parametrizados
Milhares de cursos online em vídeo, ebooks e áudiobooks.
Para testar seus conhecimentos no decorrer dos cursos online
Gerado diretamente na galeria de fotos do seu celular e enviado ao seu e-mail
Baixe nosso aplicativo pelo QR Code ou pelos links abaixo:.
+ de 10 milhões
de alunos
Certificado grátis e
válido em todo o Brasil
60 mil exercícios
gratuitos
4,8/5 classificação
nas lojas de apps
Cursos gratuitos em
vídeo, ebooks e audiobooks