Módulo 5 - Decoradores avançados
Decorador que recebe argumentos
10 min de leitura · por Cesar Gargiulo, revisado pela equipe ValorFinal e GuardiaSec · Atualizado em 01/07/2026
O que você vai aprender
- Entender por que um decorador com argumentos precisa de um nível a mais.
- Construir a fábrica de decoradores de três funções aninhadas.
- Ler a assinatura de cada nível: parâmetros, função e argumentos da chamada.
- Aplicar o padrão em um decorador de repetição configurável.
Ouvir o resumo desta aula
Um recap de cerca de 2 minutos na voz do Valim, para ouvir no trânsito ou na academia.
Ler a transcrição do resumo
Resumo da aula: Decorador que recebe argumentos.
Os objetivos desta aula. Entender por que um decorador com argumentos precisa de um nível a mais. Construir a fábrica de decoradores de três funções aninhadas. Ler a assinatura de cada nível: parâmetros, função e argumentos da chamada. Aplicar o padrão em um decorador de repetição configurável.
Veja o essencial, parte por parte.
Por que precisa de um nível a mais. Um decorador comum recebe a função; um decorador com argumentos recebe primeiro a configuração.
Montando a fábrica de três níveis. Agora o código completo.
Detalhes que valem lembrar. Conte os níveis: sem argumentos são 2 funções, com argumentos são 3.
Esse foi o resumo do essencial. Para se aprofundar, leia a aula completa e responda os exercícios.
Por que precisa de um nível a mais
Um decorador comum recebe a função e devolve o wrapper: dois níveis. Mas repare na diferença entre @registrar e @repetir(vezes=3). No segundo caso há parênteses e argumentos. Isso significa que @repetir(vezes=3) primeiro executa repetir(vezes=3), e o resultado dessa chamada é que vai decorar a função. Ou seja, repetir(vezes=3) precisa devolver um decorador. É esse nível extra que confunde no começo: a função mais de fora não recebe a função a decorar, ela recebe a configuração.
# Decorador SEM argumentos: dois niveis
@registrar
def somar(a, b):
return a + b
# equivale a: somar = registrar(somar)
# Decorador COM argumentos: tres niveis
@repetir(vezes=3)
def ola():
print("ola")
# equivale a: ola = repetir(vezes=3)(ola)
# \_______/ \__/
# fabrica funcao a decorarCom argumentos, a fábrica é chamada primeiro e devolve o decorador que envolve a função.
Guarde a linha de baixo do exemplo: ola = repetir(vezes=3)(ola). Ela mostra que há duas chamadas em sequência. A primeira, repetir(vezes=3), roda a fábrica e devolve um decorador. A segunda, (ola), aplica esse decorador à função ola. Se você consegue ler essa linha com naturalidade, o resto é só montar as três funções aninhadas na ordem certa.
Montando a fábrica de três níveis
Agora o código completo. São três funções, uma dentro da outra. A mais externa, repetir, recebe a configuração (vezes) e existe só para devolver o decorador. A do meio, decorador, é um decorador normal: recebe a função a envolver. A mais interna, wrapper, é onde o trabalho acontece, e ela enxerga tanto vezes (da fábrica) quanto funcao (do decorador), graças à closure. Leia de fora para dentro e depois de dentro para fora, até a lógica assentar.
import functools
def repetir(vezes):
def decorador(funcao):
@functools.wraps(funcao)
def wrapper(*args, **kwargs):
resultado = None
for _ in range(vezes):
resultado = funcao(*args, **kwargs)
return resultado
return wrapper
return decorador
@repetir(vezes=3)
def cumprimentar(nome):
print(f"Ola, {nome}!")
return nome
cumprimentar("Ana")
# Ola, Ana!
# Ola, Ana!
# Ola, Ana!Três níveis: repetir (configuração), decorador (recebe a função), wrapper (executa).
| Nível | Função | O que recebe | O que devolve |
|---|---|---|---|
| 1 (externo) | repetir | A configuração (vezes) | O decorador |
| 2 (meio) | decorador | A função a envolver | O wrapper |
| 3 (interno) | wrapper | Os argumentos da chamada | O resultado da função |
Os três níveis de uma fábrica de decoradores e o papel de cada um.
Detalhes que valem lembrar
Dois pontos merecem atenção. Primeiro, o functools.wraps continua indo no wrapper, o nível mais interno, exatamente como no decorador simples. Segundo, existe uma pegadinha clássica: escrever @repetir sem parênteses quando o decorador espera argumentos. Nesse caso, o Python passa a própria função como se fosse o parâmetro vezes, e tudo quebra de um jeito confuso. Se o decorador foi feito para receber configuração, ele sempre é usado com parênteses, mesmo que você aceite os valores padrão: @repetir().
Vale um último enquadramento. A fábrica não é um truque à parte, é o mesmo decorador de sempre com uma camada de configuração por fora. Se um dia você travar, escreva a linha equivalente sem a arroba, como ola = repetir(vezes=3)(ola), e siga as chamadas uma a uma. O padrão de três níveis aparece o tempo todo em bibliotecas reais, de frameworks web a ferramentas de cache, então reconhecê-lo de imediato economiza muito esforço.
Teste rápido
Por que um decorador que recebe argumentos precisa de três níveis de função?
Perguntas frequentes
- Preciso sempre usar parênteses em @repetir?
- Se o decorador foi construído como fábrica (recebe argumentos), sim, sempre com parênteses, mesmo que vazios como @repetir(). Sem os parênteses, o Python passa a função decorada como se fosse o primeiro argumento de configuração, o que gera um erro difícil de entender. Há técnicas para aceitar as duas formas, mas elas complicam o código.
- O que é closure e por que ela importa aqui?
- Closure é a capacidade de uma função interna lembrar as variáveis do escopo onde foi criada. No decorador de três níveis, o wrapper precisa enxergar tanto o vezes (definido na fábrica) quanto o funcao (definido no decorador). É a closure que mantém esses valores vivos quando o wrapper roda, tempos depois de as funções externas terem retornado.
- Posso passar mais de um argumento para a fábrica?
- Sim. A função externa aceita quantos parâmetros você quiser, com valores padrão inclusive. Por exemplo, uma fábrica repetir(vezes=3, silencioso=False) recebe dois. Todos ficam disponíveis para o wrapper por causa da closure. Só mantenha a assinatura clara, de preferência com type hints.
- Como escrevo a fábrica sem a sintaxe de arroba?
- A forma expandida ajuda a entender: minha_funcao = repetir(vezes=3)(minha_funcao). A primeira chamada roda a fábrica e devolve o decorador; a segunda aplica o decorador à função. É literalmente o que a arroba faz por baixo dos panos.
- Dá para combinar wraps com a fábrica sem problema?
- Sim, e você deve. O @functools.wraps(funcao) continua indo no wrapper, o nível mais interno, exatamente como no decorador simples. A camada extra da fábrica não muda esse detalhe. Assim a função decorada preserva nome e docstring mesmo com a configuração por fora.
Fontes
Seu progresso fica salvo neste aparelho. Assinantes sincronizam entre os aparelhos.