Decoradores no Python

Decoradores no Python

Este artigo explica decoradores no Python.

YouTube Video

Decoradores no Python

Os decoradores do Python são um recurso poderoso usado para adicionar funcionalidade adicional a funções ou métodos. Os decoradores permitem adicionar novas funcionalidades sem modificar o código existente, melhorando assim a reutilização e a manutenibilidade do código.

Noções Básicas de Decoradores

Os decoradores no Python funcionam ao receber uma função como argumento e retornar uma nova função com funcionalidades adicionais. Usando decoradores, você pode facilmente adicionar pré-processamento ou pós-processamento a funções.

Sem decoradores, alterar o comportamento de uma função exige editar diretamente a própria função. Com decoradores, você pode expandir a funcionalidade sem modificar a implementação original.

Estrutura Básica de um Decorador

 1def my_decorator(func):
 2    def wrapper():
 3        print("Before the function call")
 4        func()
 5        print("After the function call")
 6    return wrapper
 7
 8@my_decorator
 9def say_hello():
10    print("Hello!")
11
12say_hello()

Neste exemplo, a função my_decorator atua como um decorador, encapsulando a função say_hello. O decorador é aplicado usando a sintaxe @my_decorator e, quando say_hello() é chamado, o pré-processamento e o pós-processamento fornecidos pelo decorador são automaticamente executados.

Como Funcionam os Decoradores

Os decoradores funcionam nos seguintes passos.

  1. O decorador recebe uma função (ou método) como argumento.

  2. Ele define uma função envoltória para executar a função original.

  3. A função envoltória realiza processamento adicional antes ou depois de executar a função original.

  4. O decorador retorna a função envoltória. Como resultado, a função original é substituída por uma nova função decorada.

Decoradores para Funções com Argumentos

Ao aplicar um decorador a uma função com argumentos, a função envoltória deve ser ajustada para aceitar esses argumentos.

 1def my_decorator(func):
 2    def wrapper(*args, **kwargs):
 3        print("Before the function call")
 4        result = func(*args, **kwargs)
 5        print("After the function call")
 6        return result
 7    return wrapper
 8
 9@my_decorator
10def greet(name):
11    print(f"Hello, {name}!")
12
13greet("Alice")

Usando *args e **kwargs, é possível lidar com funções que aceitam qualquer número de argumentos e argumentos nomeados. Isso permite que os decoradores sejam aplicados genericamente a funções com qualquer tipo de argumentos.

Exemplos de Aplicação de Decorators

Decorator para Log

Decorators são frequentemente usados para adicionar funcionalidade de log. Por exemplo, ao criar um decorator que registra antes e depois da execução de uma função, você pode registrar quando a função é chamada e quanto tempo leva para ser executada.

 1import time
 2
 3def log_time(func):
 4    def wrapper(*args, **kwargs):
 5        start_time = time.time()
 6        print(f"Calling function '{func.__name__}'...")
 7        result = func(*args, **kwargs)
 8        end_time = time.time()
 9        print(f"Function '{func.__name__}' completed in {end_time - start_time} seconds")
10        return result
11    return wrapper
12
13@log_time
14def long_task(duration):
15    time.sleep(duration)
16    print("Task completed!")
17
18long_task(2)

Neste exemplo, o decorator log_time mede o tempo de execução de uma função e o exibe como um log. A função long_task é envolvida pelo decorator, e seu tempo de execução é registrado durante o tempo de execução.

Decorator para Gerenciamento de Permissões

Decorators também podem ser usados para o gerenciamento de permissões. Por exemplo, você pode restringir o processamento verificando se um usuário tem permissões específicas.

 1def requires_permission(user_role):
 2    def decorator(func):
 3        def wrapper(*args, **kwargs):
 4            if user_role != "admin":
 5                print("Permission denied!")
 6                return
 7            return func(*args, **kwargs)
 8        return wrapper
 9    return decorator
10
11@requires_permission("admin")
12def delete_user(user_id):
13    print(f"User {user_id} has been deleted.")
14
15delete_user(123)  # Executed
16delete_user = requires_permission("guest")(delete_user)
17delete_user(456)  # Permission denied!

O decorator requires_permission restringe a execução de uma função com base no papel do usuário. Ao consolidar essa lógica em um decorator, você pode evitar que a lógica de gerenciamento de permissões esteja espalhada por todo o seu código, melhorando sua legibilidade.

Aninhamento de Decorators

É possível aplicar vários decorators. Quando vários decorators são aplicados a uma única função, eles são executados na ordem de cima para baixo.

 1def uppercase(func):
 2    def wrapper(*args, **kwargs):
 3        result = func(*args, **kwargs)
 4        return result.upper()
 5    return wrapper
 6
 7def exclaim(func):
 8    def wrapper(*args, **kwargs):
 9        result = func(*args, **kwargs)
10        return result + "!"
11    return wrapper
12
13@uppercase
14@exclaim
15def greet(name):
16    return f"Hello, {name}"
17
18print(greet("Alice"))  # "HELLO, ALICE!"

Neste exemplo, dois decorators são aplicados à função greet. O decorator @exclaim adiciona um ponto de exclamação ao final da string, e depois o @uppercase converte a string para letras maiúsculas.

Decorators para Métodos de Classe

Decorators também podem ser usados com métodos de classe. Isso é particularmente útil quando você deseja controlar o comportamento de métodos dentro de uma classe. Ao aplicar decorators a métodos de classe, é necessário estar atento a argumentos como self ou cls.

 1def log_method_call(func):
 2    def wrapper(self, *args, **kwargs):
 3        print(f"Calling method '{func.__name__}'...")
 4        return func(self, *args, **kwargs)
 5    return wrapper
 6
 7class MyClass:
 8    @log_method_call
 9    def greet(self, name):
10        print(f"Hello, {name}")
11
12obj = MyClass()
13obj.greet("Bob")

Neste exemplo, o decorator log_method_call é aplicado ao método greet para exibir um log quando o método é chamado.

Cuidados com Decorators

Existem algumas considerações ao usar decorators. Decorators podem alterar potencialmente o nome da função original e a string de documentação (ex.: __name__ ou __doc__), por isso é recomendado usar functools.wraps para manter essa informação.

1import functools
2
3def my_decorator(func):
4    @functools.wraps(func)
5    def wrapper(*args, **kwargs):
6        print("Before the function call")
7        return func(*args, **kwargs)
8    return wrapper

Usar @functools.wraps garante que os metadados da função original sejam corretamente passados para a função de encapsulamento.

Resumo

Decoradores Python são uma ferramenta muito poderosa que permite adicionar funcionalidade adicional a funções e métodos de forma concisa. Eles podem ser usados em vários cenários para reduzir a duplicação de código e melhorar a manutenção e reutilização. Ao entender como os decoradores funcionam, você pode escrever um código mais eficiente e flexível.

Você pode acompanhar o artigo acima usando o Visual Studio Code em nosso canal do YouTube. Por favor, confira também o canal do YouTube.

YouTube Video