Módulo 12 - Decoradores
Decorador para qualquer função com args e kwargs
11 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 o wrapper original quebra com funções que recebem argumentos.
- Usar *args e **kwargs no wrapper para aceitar qualquer assinatura.
- Repassar os argumentos para a função original sem perder nenhum.
- Não esquecer de devolver o resultado da função com return.
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 para qualquer função com args e kwargs.
Os objetivos desta aula. Entender por que o wrapper original quebra com funções que recebem argumentos. Usar *args e **kwargs no wrapper para aceitar qualquer assinatura. Repassar os argumentos para a função original sem perder nenhum. Não esquecer de devolver o resultado da função com return.
Veja o essencial, parte por parte.
Por que o wrapper precisa de args e kwargs. Um wrapper sem parâmetros só funciona com funções que também não recebem argumentos.
O wrapper que aceita qualquer função. Esquecer o return dentro do wrapper faz a função decorada devolver None.
Verificando o padrão universal. A partir de agora, todo wrapper que você escrever nasce com a assinatura def wrapper(*args, **kwargs) e termina com return func(*args, **kwargs) em algum ponto.
Esse foi o resumo do essencial. Para se aprofundar, leia a aula completa e responda os exercícios.
Por que o wrapper precisa de args e kwargs
O decorador da aula anterior tinha um limite escondido. O wrapper foi escrito como def wrapper(), sem parâmetros, e por dentro chamava func() também sem argumentos. Isso funciona para dizer_ola, que não recebe nada. Mas tente decorar uma função que soma dois números e tudo desmorona: quando você chama a versão decorada passando os dois números, quem recebe esses argumentos é o wrapper, que não os espera. O Python reclama na hora que o wrapper não aceita argumentos. Um decorador de verdade precisa servir para qualquer função, e é aí que entram *args e **kwargs.
def com_moldura(func):
def wrapper(): # sem parâmetros: problema!
print("--- antes ---")
func()
print("--- depois ---")
return wrapper
@com_moldura
def somar(a, b):
return a + b
somar(2, 3)
# TypeError: wrapper() takes 0 positional arguments but 2 were givenO wrapper sem parâmetros não sabe o que fazer com os argumentos de somar.
O wrapper que aceita qualquer função
A solução é fazer o wrapper aceitar tudo e repassar tudo. Você já viu *args e **kwargs no módulo de funções avançadas: *args recolhe os argumentos posicionais numa tupla e **kwargs recolhe os nomeados num dicionário. Escrevendo def wrapper(*args, **kwargs), o wrapper aceita qualquer chamada. Dentro dele, chamamos a função original com func(*args, **kwargs), o que desempacota tudo de volta e entrega os argumentos exatos que a função esperava. Falta um detalhe fácil de esquecer e caro de depurar: devolver o resultado. Se a função original retorna um valor, o wrapper precisa fazer return func(...); caso contrário, a versão decorada passa a devolver None em silêncio.
def com_moldura(func):
def wrapper(*args, **kwargs):
print("--- antes ---")
resultado = func(*args, **kwargs) # repassa tudo
print("--- depois ---")
return resultado # devolve o valor original
return wrapper
@com_moldura
def somar(a, b):
return a + b
@com_moldura
def cumprimentar(nome, saudacao="Olá"):
return f"{saudacao}, {nome}!"
print(somar(2, 3))
# --- antes ---
# --- depois ---
# 5
print(cumprimentar("Ana", saudacao="Oi"))
# --- antes ---
# --- depois ---
# Oi, Ana!Com *args e **kwargs, o mesmo decorador serve para somar e para cumprimentar.
Verificando o padrão universal
A partir de agora, todo wrapper que você escrever nasce com a assinatura def wrapper(*args, **kwargs) e termina com return func(*args, **kwargs) em algum ponto. Esse é o formato genérico que funciona com qualquer função, receba ela zero, dois ou dez argumentos, posicionais ou nomeados. Guarde os dois cuidados: aceitar tudo na entrada com *args e **kwargs, e devolver o resultado no fim. Com isso resolvido, o decorador já é utilizável de verdade. Ainda sobra um detalhe cosmético importante, que a próxima aula ataca: o nome da função decorada passa a mostrar wrapper, e não o nome original.
Teste rápido
O wrapper chama func(*args, **kwargs) mas não usa return. O que acontece com uma função decorada que deveria retornar um valor?
Perguntas frequentes
- Preciso sempre usar *args e **kwargs juntos?
- Como padrão, sim. Usar os dois juntos no wrapper garante que ele funcione com qualquer função, aceite ela argumentos posicionais, nomeados, ambos ou nenhum. Escrever apenas *args deixaria de fora os argumentos por nome. Como regra, adote def wrapper(*args, **kwargs) para todo decorador de uso geral.
- Qual a diferença entre *args na definição e na chamada?
- Na definição do wrapper, *args recolhe os argumentos numa tupla. Na chamada func(*args), o asterisco faz o contrário: desempacota a tupla de volta em argumentos separados. É o mesmo símbolo com papéis espelhados, empacotar ao receber e desempacotar ao repassar.
- E se a função original não retorna nada?
- Não tem problema usar return func(*args, **kwargs) mesmo assim. Se a função original não devolve valor, ela devolve None por padrão, e o wrapper repassa esse None. Ou seja, colocar o return sempre é seguro e evita o esquecimento que faz sumir resultados.
- Posso guardar o resultado numa variável antes de retornar?
- Sim, e muitas vezes você vai querer. Escrever resultado = func(*args, **kwargs), fazer algo depois (como medir tempo ou registrar) e então return resultado é o padrão quando o wrapper precisa agir também após a chamada. Foi exatamente o que fizemos no exemplo com moldura.
- O wrapper precisa ter os mesmos nomes de parâmetros da função original?
- Não. É justamente essa a vantagem de *args e **kwargs: o wrapper não precisa saber os nomes nem a quantidade de argumentos. Ele aceita tudo genericamente e repassa intacto. Assim, o mesmo decorador serve para funções com assinaturas completamente diferentes.
- Isso funciona com métodos de classe também?
- Funciona. Como o self de um método é apenas o primeiro argumento posicional, ele entra em *args normalmente e é repassado sem tratamento especial. O padrão def wrapper(*args, **kwargs) mais func(*args, **kwargs) é robusto o bastante para métodos e funções comuns.
Fontes
Seu progresso fica salvo neste aparelho. Assinantes sincronizam entre os aparelhos.