🌐 Detecting your location…
📢 Advertisement — Configure AdSense in Appearance → Customize → AdSense Settings

Explicação dos decoradores Python: o guia completo de 2026

⏱️8 min read  ·  1,552 words


Python Decorators Explained: The Complete 2026 Guide

Equipe Editorial da TechPulse
Escritores de tecnologia · 24 de maio de 2026
📅 24 de maio de 2026⏱ 14 minutos de leitura📂 Programação🏷 python · decoradores · python avançado

O que é um decorador Python?

A Decorador Pythoné um padrão de design que permite adicionar um novo comportamento a uma função existente — sem alterar o código da função. Em Python, funções são objetos de primeira classe: podem ser passadas como argumentos, atribuídas a variáveis ​​e retornadas de outras funções. Os decoradores exploram essa propriedade.

O@sintaxe é apenas açúcar sintático. Quando você escreve:

@my_decorator
def greet(name):
    return f"Hello, {name}!"

Python realmente executa:

greet = my_decorator(greet)

O decorador pega a função original, envolve-a em uma nova função com comportamento extra e a retorna. O nomegreetagora aponta para a versão aprimorada.

Seu primeiro decorador: passo a passo

Vamos construir um simplesdecorador de registroque imprime uma mensagem antes e depois de chamar qualquer função.

def log_calls(func):
    """Decorator that logs function entry and exit."""
    def wrapper(*args, **kwargs):
        print(f"→ Calling {func.__name__} with args={args}, kwargs={kwargs}")
        result = func(*args, **kwargs)
        print(f"← {func.__name__} returned {result!r}")
        return result
    return wrapper

@log_calls
def add(a, b):
    return a + b

add(3, 4)
# Output:
# → Calling add with args=(3, 4), kwargs={}
# ← add returned 7

Dividindo isso:

  • log_callsrecebefunc(a função original)
  • Ele define um interiorwrapperque aceita*args, **kwargs— então funciona com qualquer assinatura de função
  • O wrapper chama o originalfunc, captura o resultado e o retorna
  • log_callsretorna o wrapper – não o resultado de chamá-lo

A correção functools.wraps

Há um bug sutil no decorador acima. Após a decoração,add.__name__retorna'wrapper', não'add'. Isso quebrahelp(), inspecte rastreamentos de pilha. Corrija comfunctools.wraps:

import functools

def log_calls(func):
    @functools.wraps(func)   # ← copies __name__, __doc__, __module__, etc.
    def wrapper(*args, **kwargs):
        print(f"→ Calling {func.__name__}")
        result = func(*args, **kwargs)
        print(f"← {func.__name__} returned {result!r}")
        return result
    return wrapper

@log_calls
def add(a, b):
    """Add two numbers."""
    return a + b

print(add.__name__)   # 'add'  ✅
print(add.__doc__)    # 'Add two numbers.'  ✅
⚠ Regra:Sempre use@functools.wraps(func)dentro de cada decorador que você escreve. Ignorá-lo causa bugs misteriosos no registro, nas estruturas de teste e nas ferramentas de documentação da API.

Decoradores com argumentos

E se você quiser configurar seu decorador? Por exemplo,@retry(times=3). Você precisa de uma camada extra de aninhamento – umfábrica de decoradores:

import functools, time

def retry(times=3, delay=1.0, exceptions=(Exception,)):
    """Retry a function up to `times` times on specified exceptions."""
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            last_err = None
            for attempt in range(1, times + 1):
                try:
                    return func(*args, **kwargs)
                except exceptions as e:
                    last_err = e
                    print(f"   Attempt {attempt}/{times} failed: {e}")
                    if attempt < times:
                        time.sleep(delay)
            raise last_err
        return wrapper
    return decorator

@retry(times=3, delay=0.5, exceptions=(ConnectionError,))
def fetch_data(url):
    # simulate a flaky network call
    raise ConnectionError("timeout")

try:
    fetch_data("https://api.example.com/data")
except ConnectionError:
    print("All retries exhausted.")

A estrutura de três níveis:retry()decorator()wrapper(). Chame-o:retry(times=3)retornadecorator, que levafunce retornawrapper.

Empilhando vários decoradores

Você pode aplicar vários decoradores a uma única função. Eles se aplicambaixo para cima(mais interno primeiro):

import functools, time

def timer(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        elapsed = time.perf_counter() - start
        print(f"{func.__name__} took {elapsed:.4f}s")
        return result
    return wrapper

def log_calls(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

@timer          # applied second (outer)
@log_calls      # applied first (inner)
def process(n):
    return sum(range(n))

process(1_000_000)
# Output:
# Calling process
# process took 0.0312s

Equivalente a:process = timer(log_calls(process)). A ordem é importante – os decoradores externos envolvem os internos.

Decoradores baseados em classe

Qualquer objeto que pode ser chamado pode ser um decorador. Usar uma classe fornece o estado entre as chamadas:

import functools

class CallCounter:
    """Counts how many times a function is called."""
    def __init__(self, func):
        functools.update_wrapper(self, func)
        self.func = func
        self.count = 0

    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f"{self.func.__name__} called {self.count} time(s)")
        return self.func(*args, **kwargs)

@CallCounter
def say_hello(name):
    print(f"Hello, {name}!")

say_hello("Alice")   # called 1 time(s)
say_hello("Bob")     # called 2 time(s)
print(say_hello.count)  # 2

@staticmethod x @classmethod x @property

Python vem com vários decoradores integrados que você usará constantemente:

class Temperature:
    _kelvin_offset = 273.15

    def __init__(self, celsius):
        self._celsius = celsius

    @property
    def fahrenheit(self):
        """Computed attribute — no () needed when accessing."""
        return self._celsius * 9/5 + 32

    @fahrenheit.setter
    def fahrenheit(self, value):
        self._celsius = (value - 32) * 5/9

    @classmethod
    def from_kelvin(cls, kelvin):
        """Factory method — creates instance from Kelvin."""
        return cls(kelvin - cls._kelvin_offset)

    @staticmethod
    def is_valid_celsius(value):
        """Utility — no access to instance or class."""
        return value >= -273.15

t = Temperature(100)
print(t.fahrenheit)                      # 212.0  (property)
t.fahrenheit = 32                        # setter
t2 = Temperature.from_kelvin(373.15)    # classmethod
print(Temperature.is_valid_celsius(-300))  # False (staticmethod)
Decorador Primeiro argumento Acesso Caso de uso
@property self Instância + classe Atributos computados, getters/setters
@classmethod cls Somente aula Métodos de fábrica, construtores alternativos
@staticmethod Nenhum Nenhum Funções utilitárias com namespace para classe

Casos de uso do mundo real

Os decoradores potencializam a maioria das estruturas Python. Aqui estão os padrões de nível de produção:

1. Armazenamento em cache com cache LRU

from functools import lru_cache

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

print(fibonacci(50))  # Instant — cached results
print(fibonacci.cache_info())  # CacheInfo(hits=48, misses=51, ...)

2. Protetor de autenticação (estilo Flask/FastAPI)

import functools

def require_auth(func):
    """Decorator for protected routes."""
    @functools.wraps(func)
    def wrapper(request, *args, **kwargs):
        token = request.headers.get("Authorization", "")
        if not token.startswith("Bearer "):
            return {"error": "Unauthorized"}, 401
        return func(request, *args, **kwargs)
    return wrapper

@require_auth
def get_user_profile(request):
    return {"user": "Alice", "email": "alice@example.com"}

3. Limitador de taxa

import functools, time
from collections import defaultdict, deque

def rate_limit(calls=10, period=60):
    """Allow at most `calls` per `period` seconds per caller."""
    history = defaultdict(deque)
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            now = time.time()
            key = args[0] if args else "default"  # use first arg as caller ID
            q = history[key]
            while q and now - q[0] > period:
                q.popleft()
            if len(q) >= calls:
                raise RuntimeError(f"Rate limit exceeded: {calls} calls per {period}s")
            q.append(now)
            return func(*args, **kwargs)
        return wrapper
    return decorator

@rate_limit(calls=5, period=60)
def send_email(user_id, message):
    print(f"Email sent to {user_id}: {message}")

Erros comuns a evitar

  • Esquecendo@functools.wraps– pausashelp(), rastreamentos e ferramentas de teste
  • Chamando a função no corpo do decoradorreturn wrapper()em vez dereturn wrapper
  • Não usando*args, **kwargs— quebra qualquer função com parâmetros
  • Estado padrão mutável compartilhado entre chamadas— use uma variável de fechamento, não um padrão mutável em nível de módulo
  • Ausentereturn resultem invólucro— engole silenciosamente o valor de retorno da função

# ❌ Wrong — returns None
def broken(func):
    def wrapper(*args, **kwargs):
        func(*args, **kwargs)   # no return!
    return wrapper

# ✅ Correct
def fixed(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)  # always return
    return wrapper

🚀 Aumente o nível de suas habilidades em Python

Agora que você entende os decoradores, explore como eles potencializam estruturas comoestruturando projetos Pythone padrões avançados. Confira nosso guia emExpressões abreviadas do Pythonpara mais truques de código Pythonic.

Perguntas frequentes

O que é um decorador Python?

Um decorador Python é uma função que recebe outra função como entrada e retorna uma versão aprimorada – sem modificar a original. O@sintaxe é uma abreviação defunc = decorator(func).

Quando devo usar decoradores?

Use decoradores para questões transversais: registro, cache, autenticação, limitação de taxa, tempo, validação de entrada — qualquer comportamento que você queira aplicar a múltiplas funções sem repetir código.

O que functools.wraps faz?

Ele copia a função original__name__, __doc__e outros atributos para o wrapper, preservando a identidade para depuração, help() e ferramentas de teste.

Os decoradores podem discutir?

Sim. Adicione uma função de fábrica externa que aceite os argumentos e retorne o decorador real. Padrão:@retry(times=3)retry()retorna o decorador.

Qual é a diferença entre @staticmethod e @classmethod?

@staticmethodnão recebe nenhum primeiro argumento implícito – é uma função simples em um namespace de classe.@classmethodrecebe a aula (cls) como primeiro argumento, útil para métodos de fábrica.

Como funcionam os decoradores empilhados?

De baixo para cima: o decorador mais próximo dodefé aplicado primeiro.@A @B def f()é igualf = A(B(f)).

✍️ Leave a Comment

Your email address will not be published. Required fields are marked *

🌐 Read in:🇩🇪 Deutsch🇧🇷 Português🇸🇦 العربية🇮🇳 हिन्दी🇧🇩 বাংলা