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.
-
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çã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.