Tamanho de fonte
Alto contraste
Altura de linha
Entrar Cadastrar

Decoradores em Python: Como Funcionam e Exemplos Práticos

Avatar de Ana Maria Gomes Ana Maria Gomes
14 minutos de leitura 28/05/2024 • Atualizado 7 dias atrás 5xp

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: 2
Testar

Nesse 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()
Testar

Quando executamos:

f = func("Hello World")
# Iniciada
# Hello World
# Finalizada

Nesse 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_hi
Testar

Aqui temos duas funções:

  • hello_function(): a função principal, que cria e retorna say_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())  # Hi

Perceba 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)
Testar

Entenda o que esse código diz:

  • A função exibe_func recebe 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 de exibe_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()
Testar

Entenda o que esse código diz:

  1. meu_decorador é uma função que recebe outra função como argumento (func);
  2. Dentro dela, wrapper() executa algo antes e depois de chamar func();
  3. Por fim, meu_decorador retorna a função wrapper, 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")
Testar

Saí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():
    ...
Testar

O 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!
Testar

Entenda o que o código diz:

  1. Definição do decorador: criamos uma função meu_decorador que aceita uma função func como argumento;
  2. Função interna: dentro de meu_decorador, definimos meu_pacote, que chama func e converte seu retorno para maiúsculas;
  3. Retorno do decoradormeu_decorador retorna meu_pacote, que é a função decorada;
  4. Uso do @: @meu_decorador é o mesmo que escrever dizer_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')
Testar

Saída:

Olá, Juliano!
Olá, Juliano!
Olá, Juliano!

Entenda o que acontece

  1. Primeira camada (repetir_vezes): recebe o argumento n (número de repetições) e retorna o decorador real;
  2. Segunda camada (decorador): recebe a função que será decorada (func);
  3. Terceira camada (wrapper): executa func n vezes, usando *args e **kwargs para que o decorador funcione com qualquer tipo de argumento;
  4. Aplicação (@repetir_vezes(3)): é o mesmo que escrever dizer_oi = repetir_vezes(3)(dizer_oi), ou seja, primeiro chamamos o decorador com n=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!
Testar

Entenda 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 @maiusculas e 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:

DecoradorFunçãoExemplo de uso
@lru_cacheFaz cache automático de resultados, evitando recomputações.Otimizar funções pesadas que recebem sempre os mesmos argumentos.
@propertyTransforma métodos em atributos de classe “controlados”.Encapsular acesso a variáveis sem quebrar a interface da classe.
@classmethodCria métodos de classe que recebem cls em vez de self.Definir comportamentos que pertencem à classe, não à instância.
@staticmethodDefine 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 wrapper
Testar

E aplicá-lo facilmente:

@monitor_tarefa
def processar_dados():
    time.sleep(1.2)
    print("Processando...")
    return "OK"

processar_dados()
Testar

Saída:

Iniciando tarefa: processar_dados
Processando...
Tarefa finalizada com sucesso. Tempo: 1.20s

Com 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 coisa
Testar

Você pode transformar o gerenciador em um decorador e simplificar o código:

@meu_decorador_contexto
def foo():
    # faz alguma coisa
Testar

Como 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!

Curso Gratuito

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
Imagem de um notebook

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
Inicie agora

Comentários

30xp
Comentar
Faça parte da discussão Crie sua conta gratuita e compartilhe
sua opinião nos comentários
Entre para a Asimov