Como usar decoradores Python para aprimorar funções

Decoradores Python são uma maneira poderosa e flexível de modificar ou aprimorar funções e métodos. Decoradores fornecem uma maneira de encapsular uma função com funcionalidade adicional, permitindo que você estenda seu comportamento sem modificar seu código real. Este artigo apresentará a você o conceito de decoradores, como criá-los e usá-los, e explorará alguns exemplos práticos.

O que é um decorador?

Um decorador é uma função que pega outra função e estende seu comportamento sem modificá-la explicitamente. Em Python, decoradores são frequentemente usados ​​para adicionar funcionalidades como registro, controle de acesso ou medição de desempenho a funções ou métodos existentes. Decoradores são aplicados a funções usando a sintaxe @decorator_name.

# Basic example of a decorator
def my_decorator(func):
    def wrapper():
        print("Something is happening before the function is called.")
        func()
        print("Something is happening after the function is called.")
    return wrapper

@my_decorator
def say_hello():
    print("Hello!")

say_hello()

Como os decoradores trabalham

Quando você aplica um decorador a uma função, o Python basicamente executa as seguintes etapas:

  1. A função decoradora é chamada com a função original como argumento.
  2. A função decoradora define uma nova função (geralmente chamada de wrapper) que aprimora ou modifica o comportamento da função original.
  3. A função decoradora retorna a nova função.
  4. Quando a função decorada é chamada, ela na verdade chama a nova função retornada pelo decorador.

Criando um decorador simples

Vamos criar um decorador simples que mede o tempo de execução de uma função. Isso é útil para testes de desempenho e otimização.

import time

def timing_decorator(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"Execution time: {end_time - start_time} seconds")
        return result
    return wrapper

@timing_decorator
def slow_function():
    time.sleep(2)
    print("Function finished!")

slow_function()

Usando decoradores com argumentos

Às vezes, você pode querer passar argumentos para seu decorador. Para conseguir isso, você precisa criar uma fábrica de decoradores — uma função que retorna um decorador. Aqui está um exemplo de um decorador que recebe um argumento para especificar uma mensagem personalizada.

def custom_message_decorator(message):
    def decorator(func):
        def wrapper(*args, **kwargs):
            print(message)
            return func(*args, **kwargs)
        return wrapper
    return decorator

@custom_message_decorator("Starting the function...")
def greet(name):
    print(f"Hello, {name}!")

greet("Alice")

Decoradores para métodos em classes

Decoradores também podem ser usados ​​com métodos dentro de classes. Usos comuns incluem registro de chamadas de método, controle de acesso e resultados de cache. Aqui está um exemplo de uso de um decorador para registrar chamadas de método em uma classe.

def log_method_call(method):
    def wrapper(self, *args, **kwargs):
        print(f"Calling {method.__name__} with arguments {args} and keyword arguments {kwargs}")
        return method(self, *args, **kwargs)
    return wrapper

class MyClass:
    @log_method_call
    def my_method(self, x, y):
        print(f"Result: {x + y}")

obj = MyClass()
obj.my_method(5, 7)

Decoradores de correntes

Você pode aplicar vários decoradores a uma única função. Eles são aplicados do decorador mais interno ao mais externo. Isso permite que você componha diferentes funcionalidades juntas. Aqui está um exemplo de encadeamento de dois decoradores:

def uppercase_decorator(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        return result.upper()
    return wrapper

def exclamation_decorator(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        return result + "!"
    return wrapper

@exclamation_decorator
@uppercase_decorator
def greet(name):
    return f"Hello, {name}"

print(greet("Alice"))

Conclusão

Decoradores são uma ferramenta versátil em Python que permite que você aprimore ou modifique o comportamento de funções e métodos. Ao usar decoradores, você pode adicionar funcionalidade reutilizável em sua base de código sem alterar a lógica central de suas funções. Seja para registro, temporização ou modificação de saída, os decoradores ajudam a manter seu código limpo e sustentável. Pratique o uso de decoradores para se tornar mais proficiente e alavancar seu poder em seus projetos Python.