Um decorador é uma função que envolve uma função
Essa frase soa abstrata, mas a mecânica é simples. Um decorador recebe uma função, devolve uma função. A função que ele devolve geralmente chama a original, com comportamento extra envolvendo a chamada.
O exemplo mais curto possível:
shout é o decorador. Recebe uma função (greet), constrói uma nova função (wrapper) que chama a original e transforma o resultado em maiúsculas, e retorna. Reatribuir greet = shout(greet) troca a original pela versão envolvida.
Esse padrão de reatribuição é tão comum que o Python deu uma sintaxe dedicada.
O @ é açúcar para uma reatribuição
@nome na linha acima de um def é equivalente a name = name(...) logo depois que a função é definida:
@shout se lê como "aplique o decorador shout a esta função". O Python roda greet = shout(greet) logo depois do def — mesma mecânica, menos digitação.
Quando você vir @nome, substitua mentalmente por funcao = nome(funcao). É tudo o que a sintaxe significa.
Lidando com argumentos
A maioria das funções recebe argumentos. Um decorador utilizável repassa. O idioma é *args, **kwargs — o jeito do Python aceitar qualquer argumento — porque o wrapper não deve se importar com o que a função envolvida espera:
*args captura todos os argumentos posicionais. **kwargs captura todos os argumentos por palavra-chave. O wrapper encaminha tudo para a função envolvida sem alterar, e depois faz qualquer trabalho extra para o qual o decorador existe — aqui, maiúsculas no resultado.
Essa é a forma que a maioria dos decoradores reais assume.
Um exemplo mais útil: timing
Imprime quanto uma função demora:
O padrão — rodar algo antes da chamada, rodar algo depois — é o que a maioria dos decoradores acaba fazendo. Logging, checagens de auth, retries e validação de entrada seguem o mesmo formato.
Preservando a identidade original: functools.wraps
Decorar uma função substitui ela, o que significa que a função envolvida perde os atributos originais __name__ e __doc__:
greet.__name__ agora é "wrapper" e a docstring sumiu. Isso quebra help(), tracebacks e qualquer ferramenta que inspecione a função.
A correção é uma linha: @functools.wraps(func) na função interna copia os metadados.
Sempre adicione @wraps(func) à função interna. Não custa nada e evita sessões de debug surpreendentes depois.
Decoradores com argumentos
Às vezes o próprio decorador precisa de configuração — "tente esta função até 3 vezes", "logue no nível DEBUG". Isso significa mais uma camada de aninhamento: uma função externa que recebe os argumentos e retorna um decorador.
Três camadas parece muita coisa. Leia de fora para dentro:
repeat(times=3)é uma chamada de função. Retorna odecorator.decoratoré o decorador de fato — recebe uma função e retorna uma envolvida.wrapperé a função envolvida que roda no momento da chamada.
Esse formato alimenta @retry(times=5), @cache(maxsize=100) e decoradores de framework como @app.route("/users"). Uma vez que você vê o padrão de três camadas, a família toda se lê igual.
Empilhando decoradores
Você pode aplicar mais de um decorador a uma função. Eles empilham de baixo para cima — o mais próximo do def roda primeiro:
add_exclaim envolve primeiro, adicionando o !. Depois shout envolve aquilo, transformando tudo em maiúsculas. A saída é HI, ROSA!.
A ordem importa. Inverter a pilha te daria HI, ROSA! com a exclamação adicionada depois do upcase — visualmente idêntico aqui, mas imagine um decorador que formata JSON: rodar antes ou depois de um decorador que loga a entrada pode produzir resultados bem diferentes.
Decoradores embutidos que você vai ver
O Python e sua biblioteca padrão vêm com alguns decoradores que você vai encontrar em código real:
@propertytransforma um método num atributo computado.@staticmethodmarca um método que não usaselfoucls.@classmethodrecebe a classe comoclsem vez de uma instância — ótimo para construtores alternativos.@functools.lru_cachememoiza resultados, então chamadas repetidas com os mesmos argumentos batem num cache.
Decoradores de framework (@app.route, @pytest.fixture, @dataclass) seguem a mesma maquinaria. Nada de especial — só funções que envolvem funções.
Quando escrever um, quando não
Escreva um decorador quando quer aplicar o mesmo comportamento a várias funções — timing, logging, retries, checagens de autorização. O ponto é que o comportamento fica fora do corpo da função.
Pule o decorador quando:
- O comportamento pertence a uma função específica. Coloque na função.
- Você só quer para teste. Uma fixture ou um parâmetro é mais claro.
- Você está tentado a empilhar quatro ou cinco. Nesse ponto, o fluxo de controle está escondido na cadeia de decoradores — um leitor tem que desenrolar cada camada para ver o que de fato roda. Uma função auxiliar direta pode ler melhor.
Decoradores são uma ferramenta afiada. Usados bem, mantêm o código DRY e a intenção óbvia. Usados mal, escondem o que o programa está fazendo. Incline-se para "óbvio" ao decidir se vai usar um.
Próxima: type hints
Decoradores são um lugar comum para encontrar type hints na prática — as funções wrapper muitas vezes anotam suas assinaturas. Type hints são uma feature pequena que compensa rápido, e vêm na sequência.
Perguntas frequentes
O que é um decorador em Python?
Um decorador é uma função que recebe outra função e retorna uma nova função — geralmente uma que envolve a original com comportamento extra. Você aplica um com @nome_do_decorador na linha acima de um def. A sintaxe @ é atalho para func = nome_do_decorador(func).
Para que decoradores são usados em Python?
Adicionar comportamento ao redor de uma função sem editar o corpo dela — logging, timing, cache, checagens de autenticação, validação de entrada, retries. Frameworks usam muito: @app.route(...) no Flask, @pytest.fixture no pytest, @property e @staticmethod embutidos.
Posso escrever meu próprio decorador?
Sim. Um decorador é só uma função que recebe uma função e retorna uma função. A maioria dos decoradores customizados envolve a chamada original numa pequena função interna que faz algo antes, depois ou ao redor dela. Use functools.wraps na função interna para preservar o nome e a docstring da função original.