Módulo 5 - Decoradores avançados

Empilhar decoradores e a ordem de aplicação

9 min de leitura · por Cesar Gargiulo, revisado pela equipe ValorFinal e GuardiaSec · Atualizado em 01/07/2026

O que você vai aprender

  • Empilhar dois ou mais decoradores sobre a mesma função.
  • Entender que a aplicação acontece de baixo para cima.
  • Entender que a execução do wrapper acontece de cima para baixo.
  • Prever a saída de uma pilha de decoradores antes de rodar.

A aplicação acontece de baixo para cima

Quando você escreve dois decoradores, um em cima do outro, a função é embrulhada em camadas. A ordem de aplicação, isto é, quem embrulha quem, vai de baixo para cima: o decorador imediatamente acima do def age primeiro, sobre a função original; o decorador de cima age depois, sobre o resultado do primeiro. É a mesma leitura de f = a(b(f)): o b é aplicado antes porque está mais para dentro dos parênteses, e o a envolve o que o b produziu.

import functools

def negrito(funcao):
    @functools.wraps(funcao)
    def wrapper(*args, **kwargs):
        return "<b>" + funcao(*args, **kwargs) + "</b>"
    return wrapper

def italico(funcao):
    @functools.wraps(funcao)
    def wrapper(*args, **kwargs):
        return "<i>" + funcao(*args, **kwargs) + "</i>"
    return wrapper

@negrito
@italico
def saudacao():
    return "ola"

print(saudacao())  # <b><i>ola</i></b>

italico envolve a função primeiro (está mais perto); negrito envolve o resultado.

A saída conta a história. O italico, mais próximo do def, embrulha ola e produz uma função que devolve i mais ola mais barra i. O negrito envolve essa função e acrescenta as tags b por fora. Por isso o resultado é b, i, ola, barra i, barra b, de fora para dentro. Se você invertesse a ordem dos dois decoradores, as tags trocariam de lugar. A pilha é literal: quem está por cima fica por fora.

A execução como camadas de cebola

Há dois momentos distintos e é fácil confundi-los. A aplicação, que monta as camadas, acontece uma vez, quando o Python define a função, e vai de baixo para cima. A execução, que percorre as camadas, acontece toda vez que você chama a função, e vai de fora para dentro: o wrapper do decorador de cima roda primeiro, faz sua parte, chama o wrapper de baixo, que faz a dele e chama a função original. Na volta, cada camada completa seu trabalho. É o modelo de cebola.

import functools

def faixa(nome):
    def decorador(funcao):
        @functools.wraps(funcao)
        def wrapper(*args, **kwargs):
            print(f"entra {nome}")
            resultado = funcao(*args, **kwargs)
            print(f"sai {nome}")
            return resultado
        return wrapper
    return decorador

@faixa("externo")
@faixa("interno")
def tarefa():
    print("executando tarefa")

tarefa()
# entra externo
# entra interno
# executando tarefa
# sai interno
# sai externo

O de cima (externo) entra primeiro e sai por último; o de baixo (interno) fica no meio.

MomentoDireçãoQuando acontece
Aplicação (montar camadas)De baixo para cimaUma vez, ao definir a função
Execução (percorrer camadas)De fora para dentro e de voltaA cada chamada da função

Aplicação e execução andam em sentidos diferentes; separar as duas evita confusão.

Quando a ordem muda o resultado

Nem sempre a ordem importa, mas quando importa, importa muito. Um caso clássico é combinar um decorador que verifica permissão com um que faz cache. Se o cache fica por fora, uma resposta guardada pode ser servida sem checar a permissão de novo, o que é um problema de segurança. Se a verificação fica por fora, ela roda sempre, antes de qualquer cache. Por isso vale desenhar a pilha de propósito, perguntando o que precisa acontecer primeiro em cada chamada.

A boa notícia é que não há regra a decorar, só um modelo a entender. Aplicação de baixo para cima, execução de fora para dentro. Sempre que uma pilha de decoradores confundir, transforme-a na forma com parênteses e leia devagar. Com um pouco de prática, você passa a prever a saída de uma pilha só de olhar, e a escolher a ordem que produz o comportamento certo, em vez de descobrir por tentativa e erro.

Teste rápido

Em @a seguido de @b sobre uma função f, qual é a equivalência correta?

Perguntas frequentes

A ordem dos decoradores sempre muda o resultado?
Nem sempre. Se os decoradores fazem coisas independentes, que não interferem uma na outra, a ordem pode não afetar a saída. Mas quando um decorador transforma o retorno ou depende de rodar antes de outro (como segurança antes de cache), a ordem muda o comportamento. Por segurança, desenhe a pilha de propósito.
Como lembro qual decorador roda primeiro na execução?
O que está mais em cima, mais longe do def, roda primeiro na execução e termina por último. Pense em camadas de cebola: o wrapper de fora começa, chama o de dentro, que chama a função original, e a volta acontece na ordem inversa. Aplicação é de baixo para cima; execução é de fora para dentro.
Existe limite de quantos decoradores posso empilhar?
Não há limite prático imposto pela linguagem, mas há um limite de bom senso. Cada camada adiciona uma chamada de função e uma indireção a mais. Duas ou três costumam ser suficientes e legíveis; pilhas muito altas ficam difíceis de entender e depurar. Se precisar de muitas, talvez um único decorador mais completo seja melhor.
Por que o functools.wraps é importante ao empilhar?
Porque sem ele cada camada apagaria o nome da anterior, e a função final se apresentaria como wrapper, sem relação com a original. Com wraps em cada nível, os metadados da função original atravessam todas as camadas e chegam intactos ao topo. Em pilhas de decoradores, esquecer o wraps confunde ainda mais a depuração.
Qual a forma mais segura de prever a saída de uma pilha?
Reescrever a pilha na forma com parênteses. Uma sequência @a, @b, @c sobre f vira f = a(b(c(f))). Aí você lê de dentro para fora para entender a aplicação e de fora para dentro para entender a execução. Essa tradução mecânica elimina quase toda a confusão.

Fontes

Seu progresso fica salvo neste aparelho. Assinantes sincronizam entre os aparelhos.