O que são decoradores Python?
Os decoradores Python modificam o comportamento de uma função sem alterar sua lógica interna. Pense neles como “embalagens inteligentes” para suas funções.
Assim como uma embalagem protege e adiciona informações a um produto sem mudar o que está dentro, os decoradores adicionam funcionalidades às suas funções sem mexer no código original.
Por exemplo, um decorador pode registrar automaticamente cada vez que uma função é executada ou garantir que apenas usuários autenticados possam acessá-la. Com isso, o código fica mais limpo, automatizado e “Pythonico”, ou seja, mais alinhado com as boas práticas da linguagem.
Por que usar decoradores Python?
Você provavelmente já se pegou repetindo o mesmo trecho de código em várias funções, seja para registrar logs, medir o tempo de execução ou validar permissões. Os decoradores em Python resolvem exatamente esse problema.
Eles permitem automatizar tarefas repetitivas e padronizar comportamentos, sem alterar a função original. Por isso, são muito usados em frameworks como Flask, FastAPI e Django, para verificar autenticação, tratar erros, medir tempo de execução ou até gerenciar cache.
Principais vantagens dos decoradores
- Evitam repetição de código;
- Mantêm o código mais limpo e modular;
- Adicionam funcionalidades sem alterar funções originais;
- São ideais para logs, validações, autenticação e medições de performance.
Entendendo a base: funções são objetos em Python
Antes de aprender como os decoradores funcionam, é importante dominar um conceito básico: em Python, funções são objetos de primeira classe (first-class objects).
Isso significa que você pode atribuir uma função a uma variável, passá-la como argumento para outra função ou até retorná-la de dentro de outra, assim como faria com listas, strings ou números.
Veja este exemplo simples:
def func():
return 2
minha_funcao = func
retorno = minha_funcao()
print(retorno)
# Saída: 2TestarNesse exemplo, a função func foi atribuída à variável minha_funcao e chamada normalmente em seguida. Isso mostra que funções podem ser tratadas como valores e é justamente essa flexibilidade que torna possível a criação de decoradores.
Funções dentro de funções
Outra característica importante do Python é que podemos definir funções dentro de outras funções. Isso é chamado de função aninhada (nested function).
Esse recurso permite criar comportamentos internos que só existem dentro daquela função principal, ajudando a organizar o código e controlar o escopo de variáveis e funcionalidades.
Veja este exemplo simples:
def func(string):
def wrapper():
print("Iniciada")
print(string)
print("Finalizada")
return wrapper()TestarQuando executamos:
f = func("Hello World")
# Iniciada
# Hello World
# FinalizadaNesse exemplo, o Python executa wrapper() dentro de func. Esse conceito, de uma função que retorna outra, é o que torna os decoradores possíveis.
Retornando funções de outras funções
Em Python, uma função também pode retornar outra função como resultado. Isso significa que, em vez de devolver um valor simples (como um número ou texto), ela pode gerar uma nova função pronta para ser usada.
Essa técnica permite criar funções personalizadas, com comportamentos específicos baseados em parâmetros ou condições.
Veja este exemplo:
def hello_function():
def say_hi():
return "Hi"
return say_hiTestarAqui temos duas funções:
hello_function(): a função principal, que cria e retornasay_hi;say_hi(): a função interna, que retorna a string"Hi".
Agora, veja isso em ação:
hello = hello_function()
print(hello) # <function hello_function.<locals>.say_hi at 0x000001>
print(hello()) # HiPerceba que hello_function() não retorna o texto "Hi", mas sim a função say_hi em si. Somente quando chamamos hello() é que a mensagem “Hi” é exibida.
Passando funções como argumentos
Outra característica importante do Python é que funções podem ser passadas como argumentos para outras funções. Isso é possível porque, conforme explicado, funções são objetos de primeira classe e esse comportamento é uma das bases dos decoradores.
Veja este exemplo:
def exibe_func(f):
print(f'Objeto de função recebido: {f}')
print(f'Nome da função: "{f.__name__}"')
def hello():
return 'Oi, José!'
exibe_func(hello)TestarEntenda o que esse código diz:
- A função
exibe_funcrecebe outra função (hello) como argumento; - Dentro dela, usamos
f.__name__para exibir o nome da função recebida; - Não chamamos
hello(), apenas passamos sua referência, o que permite manipulá-la dentro deexibe_func.
Saída:
Objeto de função recebido: <function hello at 0x000001>
Nome da função: "hello"Como criar seu primeiro decorador Python
Agora que você já entendeu que funções podem ser passadas e retornadas como objetos, chegou a hora de criar seu primeiro decorador na prática. Primeiro, você vai aprender a criar o decorador manualmente.
Veja este exemplo:
def meu_decorador(func):
def wrapper():
print("Antes da função ser executada.")
func()
print("Depois da função ser executada.")
return wrapper
def saudacao():
print("Olá, Asimover!")
# Aplicando o decorador manualmente
decorada = meu_decorador(saudacao)
decorada()TestarEntenda o que esse código diz:
meu_decoradoré uma função que recebe outra função como argumento (func);- Dentro dela,
wrapper()executa algo antes e depois de chamarfunc(); - Por fim,
meu_decoradorretorna a funçãowrapper, criando uma nova versão da função original.
Saída:
Olá, Asimover!Tornando o decorador genérico com *args e **kwargs
Até aqui, nosso decorador funciona apenas com funções que não recebem parâmetros. Mas e se quisermos decorar uma função que recebe argumentos?
Para isso, usamos os parâmetros especiais *args e **kwargs dentro da função wrapper(). Eles permitem aceitar qualquer número de argumentos posicionais e nomeados, tornando o decorador compatível com qualquer função.
Veja como fica:
def meu_decorador(func):
def wrapper(*args, **kwargs):
print("Antes da função.")
resultado = func(*args, **kwargs)
print("Depois da função.")
return resultado
return wrapper
def dizer_oi(nome):
print(f"Olá, {nome}!")
decorada = meu_decorador(dizer_oi)
decorada("Juliano")TestarSaída:
Olá, Juliano!Usando o símbolo @ (syntax sugar)
Agora que você já sabe criar um decorador manualmente, é hora de usar o atalho mais elegante e legível do Python: o símbolo @.
Essa sintaxe é chamada de syntax sugar porque não muda o funcionamento, apenas simplifica a forma de aplicar o decorador.
Seu funcionamento é simples. Quando escrevemos:
@meu_decorador
def funcao():
...TestarO Python interpreta internamente assim:
def funcao():
...
funcao = meu_decorador(funcao)Ou seja, o @ é apenas um atalho para aplicar a função decoradora sobre a função original, deixando o código mais limpo e fácil de entender.
Exemplo prático
Vamos criar um decorador simples que transforma o retorno de uma função em letras maiúsculas:
def meu_decorador(func):
def meu_pacote(*args, **kwargs):
retorno = func(*args, **kwargs)
return retorno.upper()
return meu_pacote
@meu_decorador
def dizer_oi(nome):
return f'Olá, {nome}!'
print(dizer_oi('Juliano'))
# Saída: OLÁ, JULIANO!TestarEntenda o que o código diz:
- Definição do decorador: criamos uma função
meu_decoradorque aceita uma funçãofunccomo argumento; - Função interna: dentro de
meu_decorador, definimosmeu_pacote, que chamafunce converte seu retorno para maiúsculas; - Retorno do decorador:
meu_decoradorretornameu_pacote, que é a função decorada; - Uso do @:
@meu_decoradoré o mesmo que escreverdizer_oi = meu_decorador(dizer_oi), só que de forma mais clara e Pythonica.
Decoradores com argumentos
Até aqui, vimos que um decorador pode receber uma função e devolver outra. Mas e se quisermos que o decorador também receba seus próprios parâmetros?
Para isso, você deve criar uma camada extra de funções. Assim, o decorador pode personalizar seu comportamento com base nos argumentos que você passar.
Veja este exemplo:
Exemplo prático
Vamos criar um decorador que repete a execução de uma função um número específico de vezes:
def repetir_vezes(n):
def decorador(func):
def wrapper(*args, **kwargs):
for _ in range(n):
resultado = func(*args, **kwargs)
return resultado
return wrapper
return decorador
@repetir_vezes(3)
def dizer_oi(nome):
print(f'Olá, {nome}!')
dizer_oi('Juliano')TestarSaída:
Olá, Juliano!
Olá, Juliano!
Olá, Juliano!Entenda o que acontece
- Primeira camada (
repetir_vezes): recebe o argumenton(número de repetições) e retorna o decorador real; - Segunda camada (
decorador): recebe a função que será decorada (func); - Terceira camada (
wrapper): executafuncn vezes, usando*argse**kwargspara que o decorador funcione com qualquer tipo de argumento; - Aplicação (
@repetir_vezes(3)): é o mesmo que escreverdizer_oi = repetir_vezes(3)(dizer_oi), ou seja, primeiro chamamos o decorador comn=3, e depois o aplicamos à função.
Decoradores aninhados
O Python permite aplicar vários decoradores à mesma função, empilhando comportamentos diferentes. Essa técnica é chamada de decoradores aninhados (nested decorators).
Na prática, é como envolver uma função em várias camadas, e cada camada adiciona uma funcionalidade extra.
Exemplo prático
Vamos criar dois decoradores: um que transforma o texto em maiúsculas e outro que repete o resultado duas vezes.
def maiusculas(func):
def wrapper(*args, **kwargs):
retorno = func(*args, **kwargs)
return retorno.upper()
return wrapper
def repetir(func):
def wrapper(*args, **kwargs):
resultado = func(*args, **kwargs)
return resultado * 2
return wrapper
@repetir
@maiusculas
def saudacao(nome):
return f'Olá, {nome}!'
print(saudacao('Juliano'))
# Saída: OLÁ, JULIANO!OLÁ, JULIANO!TestarEntenda o que acontece
- Definição dos decoradores:
maiúsculas(converte o texto de retorno para letras maiúsculas)/repetir(duplica o texto retornado); - Ordem de execução: os decoradores são aplicados de baixo para cima. Isso significa que o Python primeiro aplica
@maiusculase depois@repetir. Em outras palavras:saudacao = repetir(maiusculas(saudacao)); - Resultado final: a função
saudacao()retorna o texto em letras maiúsculas duas vezes seguidas.
Aplicações práticas: exemplos de decoradores Python
Agora que você já sabe como criar seus próprios decoradores, veja como eles podem ser usados na vida real.
Decoradores prontos da linguagem
O Python já traz vários decoradores embutidos que você pode usar no dia a dia, tais como:
| Decorador | Função | Exemplo de uso |
|---|---|---|
@lru_cache | Faz cache automático de resultados, evitando recomputações. | Otimizar funções pesadas que recebem sempre os mesmos argumentos. |
@property | Transforma métodos em atributos de classe “controlados”. | Encapsular acesso a variáveis sem quebrar a interface da classe. |
@classmethod | Cria métodos de classe que recebem cls em vez de self. | Definir comportamentos que pertencem à classe, não à instância. |
@staticmethod | Define métodos estáticos sem referência à instância. | Criar utilitários internos dentro da classe. |
Decoradores personalizados na prática
Em projetos maiores, especialmente em frameworks e bibliotecas, decoradores ajudam a padronizar tarefas e eliminar código repetido.
Por exemplo: vamos supor que você tem várias funções responsáveis por executar tarefas assíncronas (workers) que processam filas de dados. Você pode querer registrar automaticamente o tempo de execução, o status da tarefa e o resultado de cada função.
Em vez de escrever esse código em todas as funções, você pode criar um decorador:
import time
def monitor_tarefa(func):
def wrapper(*args, **kwargs):
inicio = time.time()
print(f"Iniciando tarefa: {func.__name__}")
try:
resultado = func(*args, **kwargs)
status = "sucesso"
return resultado
except Exception as e:
status = f"falha ({e})"
finally:
duracao = time.time() - inicio
print(f"Tarefa finalizada com {status}. Tempo: {duracao:.2f}s")
return wrapperTestarE aplicá-lo facilmente:
@monitor_tarefa
def processar_dados():
time.sleep(1.2)
print("Processando...")
return "OK"
processar_dados()TestarSaída:
Iniciando tarefa: processar_dados
Processando...
Tarefa finalizada com sucesso. Tempo: 1.20sCom esse padrão, cada nova função decorada registra automaticamente seu próprio log, sem repetir código.
Decoradores e gerenciadores de contexto
Outra aplicação interessante é usar decoradores para encapsular gerenciadores de contexto (with).
Por exemplo, em vez de escrever assim:
def foo():
with MyContextManager() as m:
# faz alguma coisaTestarVocê pode transformar o gerenciador em um decorador e simplificar o código:
@meu_decorador_contexto
def foo():
# faz alguma coisaTestarComo dominar os fundamentos do Python e aplicar decoradores na prática
Os decoradores em Python estão entre as ferramentas mais poderosas da linguagem. Com eles, você pode automatizar tarefas, criar logs, medir tempo de execução, validar dados e muito mais.
É o tipo de recurso que mostra por que o Python é tão querido por quem gosta de resolver problemas com eficiência e clareza.
Quer aprender Python do zero e aplicar esses conceitos na prática? Faça o curso gratuito Python para Iniciantes, da Asimov Academy.
Nele, você vai aprender sobre variáveis, loops e funções e ainda criar seu primeiro projeto Python em apenas 2 horas.
Comece agora e descubra como transformar ideias em código!
Seu primeiro projeto Python – curso grátis com certificado!
Vá do zero ao primeiro projeto em apenas 2 horas com o curso Python para Iniciantes.
Comece agora
Cursos de programação gratuitos com certificado
Aprenda a programar e desenvolva soluções para o seu trabalho com Python para alcançar novas oportunidades profissionais. Aqui na Asimov você encontra:
- Conteúdos gratuitos
- Projetos práticos
- Certificados
- +20 mil alunos e comunidade exclusiva
- Materiais didáticos e download de código
Comentários
30xp