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çãosay_hello
. O decorador é aplicado usando a sintaxe@my_decorator
e, quandosay_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.
-
O decorador recebe uma função (ou método) como argumento.
-
Ele define uma função envoltória para executar a função original.
-
A função envoltória realiza processamento adicional antes ou depois de executar a função original.
-
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çãolong_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étodogreet
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
3# Decorator without functools.wraps
4def bad_decorator(func):
5 def wrapper(*args, **kwargs):
6 print("Before the function call")
7 return func(*args, **kwargs)
8 return wrapper
9
10# Decorator with functools.wraps
11def good_decorator(func):
12 @functools.wraps(func)
13 def wrapper(*args, **kwargs):
14 print("Before the function call")
15 return func(*args, **kwargs)
16 return wrapper
17
18# Apply decorators
19@bad_decorator
20def my_function_bad():
21 """This is the original docstring for my_function_bad."""
22 print("Executing my_function_bad")
23
24@good_decorator
25def my_function_good():
26 """This is the original docstring for my_function_good."""
27 print("Executing my_function_good")
28
29# Run functions
30print("=== Without functools.wraps ===")
31my_function_bad()
32print("Function name:", my_function_bad.__name__)
33print("Docstring:", my_function_bad.__doc__)
34
35print("\n=== With functools.wraps ===")
36my_function_good()
37print("Function name:", my_function_good.__name__)
38print("Docstring:", my_function_good.__doc__)
- 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.