---
title: "Decoradores Python: Guia Pratico — 2025 | Python Brasil"
url: "https://python.dev.br/blog/decoradores-python-guia-pratico/"
markdown_url: "https://python.dev.br/blog/decoradores-python-guia-pratico.MD"
description: "Aprenda a criar e usar decoradores em Python com exemplos praticos. Domine closures, functools.wraps e decoradores com argumentos no seu codigo."
date: "2025-07-15"
author: "Equipe Python Brasil"
---

# Decoradores Python: Guia Pratico — 2025 | Python Brasil

Aprenda a criar e usar decoradores em Python com exemplos praticos. Domine closures, functools.wraps e decoradores com argumentos no seu codigo.


Decoradores sao uma das funcionalidades mais elegantes do Python. Eles permitem modificar o comportamento de funcoes e classes sem alterar o codigo original. Neste guia pratico, a gente vai entender como decoradores funcionam por baixo dos panos e criar varios exemplos uteis para o dia a dia.

## O Que Sao Decoradores?

Um decorador e uma funcao que recebe outra funcao como argumento, adiciona alguma funcionalidade e retorna uma nova funcao. Em termos simples, e um wrapper que envolve uma funcao existente.

A sintaxe com `@` e apenas um atalho sintatico. Estas duas formas sao equivalentes:

```python
# Com sintaxe @
@meu_decorador
def minha_funcao():
    pass

# Equivalente sem @
def minha_funcao():
    pass
minha_funcao = meu_decorador(minha_funcao)
```

Para entender decoradores, primeiro precisamos entender que em Python, funcoes sao objetos de primeira classe. Isso significa que podemos passar funcoes como argumentos, retorna-las de outras funcoes e atribui-las a variaveis.

## Criando Seu Primeiro Decorador

Vamos comecar com um decorador simples que registra quando uma funcao e chamada:

```python
import functools

def log_chamada(func):
    """Decorador que registra chamadas de funcao."""
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print(f"Chamando {func.__name__} com args={args}, kwargs={kwargs}")
        resultado = func(*args, **kwargs)
        print(f"{func.__name__} retornou {resultado}")
        return resultado
    return wrapper

@log_chamada
def somar(a, b):
    """Soma dois numeros."""
    return a + b

@log_chamada
def saudacao(nome, idioma="pt"):
    """Retorna uma saudacao."""
    if idioma == "pt":
        return f"Ola, {nome}!"
    return f"Hello, {nome}!"

# Uso
somar(3, 5)
# Chamando somar com args=(3, 5), kwargs={}
# somar retornou 8

saudacao("Maria", idioma="en")
# Chamando saudacao com args=('Maria',), kwargs={'idioma': 'en'}
# saudacao retornou Hello, Maria!
```

Repare no uso de `functools.wraps(func)`. Sem ele, a funcao decorada perde seu nome e docstring originais. Sempre use `functools.wraps` nos seus decoradores.

## Decorador de Temporizacao

Um caso de uso muito comum e medir o tempo de execucao de funcoes:

```python
import functools
import time

def cronometrar(func):
    """Mede o tempo de execucao de uma funcao."""
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        inicio = time.perf_counter()
        resultado = func(*args, **kwargs)
        fim = time.perf_counter()
        duracao = fim - inicio
        print(f"{func.__name__} executou em {duracao:.4f} segundos")
        return resultado
    return wrapper

@cronometrar
def processar_dados(n):
    """Simula processamento pesado."""
    total = sum(i ** 2 for i in range(n))
    return total

processar_dados(1_000_000)
# processar_dados executou em 0.1523 segundos
```

## Decoradores com Argumentos

Quando voce precisa que o decorador aceite parametros, e necessario criar mais um nivel de funcao aninhada:

```python
import functools

def repetir(vezes):
    """Decorador que repete a execucao de uma funcao."""
    def decorador(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            resultado = None
            for i in range(vezes):
                print(f"Execucao {i + 1} de {vezes}")
                resultado = func(*args, **kwargs)
            return resultado
        return wrapper
    return decorador

@repetir(vezes=3)
def enviar_notificacao(mensagem):
    """Envia uma notificacao."""
    print(f"Notificacao: {mensagem}")
    return True

enviar_notificacao("Backup concluido")
# Execucao 1 de 3
# Notificacao: Backup concluido
# Execucao 2 de 3
# Notificacao: Backup concluido
# Execucao 3 de 3
# Notificacao: Backup concluido
```

## Decorador de Cache (Memoizacao)

O Python ja oferece `functools.lru_cache`, mas e instrutivo criar o seu proprio:

```python
import functools

def cache_simples(func):
    """Cache simples para funcoes puras."""
    _cache = {}

    @functools.wraps(func)
    def wrapper(*args):
        if args in _cache:
            print(f"Cache hit para {args}")
            return _cache[args]
        resultado = func(*args)
        _cache[args] = resultado
        return resultado

    wrapper.cache_info = lambda: f"Tamanho do cache: {len(_cache)}"
    wrapper.cache_limpar = lambda: _cache.clear()
    return wrapper

@cache_simples
def fibonacci(n):
    """Calcula o n-esimo numero de Fibonacci."""
    if n < 2:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

print(fibonacci(30))  # 832040
print(fibonacci.cache_info())  # Tamanho do cache: 31
```

Na pratica, use `functools.lru_cache` que ja vem otimizado:

```python
from functools import lru_cache

@lru_cache(maxsize=128)
def fibonacci_otimizado(n):
    if n < 2:
        return n
    return fibonacci_otimizado(n - 1) + fibonacci_otimizado(n - 2)
```

## Decorador de Validacao

Decoradores sao otimos para validar entradas antes de executar a logica principal:

```python
import functools

def validar_tipos(**tipos_esperados):
    """Valida os tipos dos argumentos de uma funcao."""
    def decorador(func):
        @functools.wraps(func)
        def wrapper(**kwargs):
            for param, tipo in tipos_esperados.items():
                if param in kwargs and not isinstance(kwargs[param], tipo):
                    raise TypeError(
                        f"Parametro '{param}' deve ser {tipo.__name__}, "
                        f"recebeu {type(kwargs[param]).__name__}"
                    )
            return func(**kwargs)
        return wrapper
    return decorador

@validar_tipos(nome=str, idade=int)
def cadastrar_usuario(nome, idade):
    """Cadastra um usuario no sistema."""
    return {"nome": nome, "idade": idade}

# Funciona normalmente
cadastrar_usuario(nome="Ana", idade=28)

# Levanta TypeError
# cadastrar_usuario(nome="Ana", idade="vinte")
# TypeError: Parametro 'idade' deve ser int, recebeu str
```

## Decorador de Retry

Muito util para operacoes que podem falhar temporariamente, como chamadas de rede:

```python
import functools
import time

def retry(tentativas=3, delay=1, excecoes=(Exception,)):
    """Repete a funcao em caso de falha."""
    def decorador(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            ultima_excecao = None
            for tentativa in range(1, tentativas + 1):
                try:
                    return func(*args, **kwargs)
                except excecoes as e:
                    ultima_excecao = e
                    print(f"Tentativa {tentativa}/{tentativas} falhou: {e}")
                    if tentativa < tentativas:
                        time.sleep(delay)
            raise ultima_excecao
        return wrapper
    return decorador

@retry(tentativas=3, delay=2, excecoes=(ConnectionError, TimeoutError))
def buscar_dados_api(url):
    """Busca dados de uma API externa."""
    import requests
    response = requests.get(url, timeout=5)
    response.raise_for_status()
    return response.json()
```

## Empilhando Decoradores

Voce pode aplicar varios decoradores a uma mesma funcao. Eles sao aplicados de baixo para cima:

```python
@cronometrar
@log_chamada
def calcular(x, y):
    return x * y

# Equivale a: cronometrar(log_chamada(calcular))
```

## Decoradores em Classes

Decoradores tambem podem ser implementados como classes usando o metodo `__call__`:

```python
class ContadorChamadas:
    """Conta quantas vezes uma funcao foi chamada."""

    def __init__(self, func):
        functools.update_wrapper(self, func)
        self.func = func
        self.chamadas = 0

    def __call__(self, *args, **kwargs):
        self.chamadas += 1
        print(f"{self.func.__name__} foi chamada {self.chamadas} vezes")
        return self.func(*args, **kwargs)

@ContadorChamadas
def processar(dado):
    return dado.upper()

processar("teste")   # processar foi chamada 1 vezes
processar("outro")   # processar foi chamada 2 vezes
```

## Boas Praticas

Ao trabalhar com decoradores, siga estas recomendacoes:

- Sempre use `functools.wraps` para preservar metadados da funcao original
- Mantenha decoradores com responsabilidade unica, cada um faz apenas uma coisa
- Documente claramente o que o decorador faz e quais parametros aceita
- Teste decoradores separadamente, como qualquer outra funcao
- Evite efeitos colaterais inesperados dentro de decoradores
- Prefira decoradores do stdlib como `lru_cache`, `property` e `staticmethod` quando possiveis

## Conclusao

Decoradores sao uma ferramenta poderosa que torna o codigo Python mais limpo e reutilizavel. Com o entendimento de closures e funcoes de ordem superior, voce pode criar decoradores para logging, cache, validacao, controle de acesso e muito mais. Comece com os exemplos deste guia e adapte-os para as necessidades do seu projeto. A pratica constante vai tornar o uso de decoradores algo natural no seu dia a dia como desenvolvedor Python.

Se voce se interessa por metaprogramacao, vale explorar tambem o sistema de macros procedurais de <a href="https://rustlang.com.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'rustlang.com.br' })">Rust</a>, que oferece transformacoes de codigo em tempo de compilacao, e as annotation processors de <a href="https://kotlin.dev.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'kotlin.dev.br' })">Kotlin</a>, que resolvem problemas semelhantes no ecossistema JVM.
