Os decoradores em Python são uma ferramenta poderosa que permite modificar ou aprimorar a funcionalidade de funções, classes e métodos de forma transparente e elegante. Neste artigo, vamos discutir o que são decoradores em Python e como usá-los.
O que são decoradores em Python?
Em Python, um decorador é uma função que recebe outra função como entrada, executa alguma ação com a função de entrada e retorna a função modificada como saída. Na prática, um decorador é usado para envolver uma função em uma camada adicional de funcionalidade.
Por exemplo, suponha que temos uma função simples que calcula a soma de dois números:
def soma(a, b):
return a + b
Agora, se quisermos adicionar a funcionalidade de imprimir uma mensagem antes de executar a função soma
, podemos criar um decorador:
def imprimir_mensagem(func):
def wrapper(*args, **kwargs):
print("Executando função...")
return func(*args, **kwargs)
return wrapper
Aqui, a função imprimir_mensagem
é o decorador que envolve a função soma
em uma camada adicional de funcionalidade. A função wrapper
é o que chamamos de função interna do decorador, que é responsável por executar a ação adicional antes de chamar a função de entrada func
.
Observe que a função wrapper
recebe argumentos *args
e **kwargs
. Isso significa que ele pode receber qualquer número de argumentos posicionais ou nomeados e repassá-los para a função de entrada func
.
Para usar o decorador imprimir_mensagem
, basta aplicá-lo à função soma
:
@imprimir_mensagem
def soma(a, b):
return a + b
Aqui, usamos a sintaxe @decorator
para aplicar o decorador imprimir_mensagem
à função soma
. Isso é equivalente a chamar a função imprimir_mensagem
com a função soma
como argumento e substituir a função original pela função modificada que o decorador retorna.
Agora, sempre que chamarmos a função soma
, a mensagem "Executando função..." será impressa antes de calcular a soma.
>>> soma(2, 3)
Executando função...
5
Decoradores de classe
Os decoradores em Python também podem ser aplicados a classes. Um decorador de classe é uma função que recebe uma classe como entrada, executa alguma ação com a classe de entrada e retorna a classe modificada como saída.
Por exemplo, podemos ter uma classe simples que define um retângulo:
class Retangulo:
def __init__(self, largura, altura):
self.largura = largura
self.altura = altura
def area(self):
return self.largura * self.altura
Agora, se quisermos adicionar a funcionalidade de verificar se a largura e altura são positivas antes de criar um objeto Retangulo
, podemos criar um decorador de classe:
def verificar_positividade(cls):
class RetanguloPositivo:
def __init__(self, largura, altura):
if largura <= 0 or altura <= 0:
raise ValueError("Largura e altura devem ser positivas.")
self.largura = largura
self.altura = altura
def area(self):
return self.largura * self.altura
return RetanguloPositivo
Nesse caso, o decorador verificar_positividade
recebe a classe Retangulo
como entrada, define uma nova classe RetanguloPositivo
que estende a classe original e adiciona a funcionalidade de verificar se a largura e a altura são positivas. Por fim, retorna a nova classe RetanguloPositivo
.
Para usar o decorador de classe verificar_positividade
, basta aplicá-lo à classe Retangulo
:
@verificar_positividade
class Retangulo:
def __init__(self, largura, altura):
self.largura = largura
self.altura = altura
def area(self):
return self.largura * self.altura
Aqui, usamos a sintaxe @decorator
para aplicar o decorador verificar_positividade
à classe Retangulo
. Isso é equivalente a chamar a função verificar_positividade
com a classe Retangulo
como argumento e substituir a classe original pela classe modificada que o decorador retorna.
Agora, sempre que criarmos um objeto Retangulo
, a verificação de positividade será realizada automaticamente:
>>> r = Retangulo(2, 3)
>>> r.area()
6
>>> r = Retangulo(-2, 3)
ValueError: Largura e altura devem ser positivas.
Decoradores com argumentos
Os decoradores em Python também podem receber argumentos. Isso permite a criação de decoradores mais genéricos e flexíveis que podem ser personalizados de acordo com as necessidades específicas.
Por exemplo, podemos ter um decorador que permite medir o tempo de execução de uma função:
import time
def medir_tempo(func):
def wrapper(*args, **kwargs):
inicio = time.time()
resultado = func(*args, **kwargs)
fim = time.time()
print(f"Tempo de execução: {fim - inicio:.2f} segundos")
return resultado
return wrapper
Aqui, a função medir_tempo
é um decorador que envolve a função de entrada func
em uma camada adicional de funcionalidade que mede o tempo de execução. Observe que a função wrapper
ainda recebe argumentos *args
e **kwargs
para que possa lidar com funções com diferentes assinaturas.
No entanto, se quisermos personalizar o texto da mensagem de tempo de execução, podemos criar um decorador com argumentos:
import time
def medir_tempo(texto):
def decorator(func):
def wrapper(*args, **kwargs):
inicio = time.time()
resultado = func(*args, **kwargs)
fim = time.time()
print(f"{texto}: {fim - inicio:.2f} segundos")
return resultado
return wrapper
return decorator
Observe que agora a função medir_tempo
é um decorador com um argumento texto
, que é usado para personalizar a mensagem de tempo de execução. Em vez de retornar a função wrapper
diretamente, a função decorator
é retornada em vez disso, permitindo que a função de entrada func
seja passada como argumento e retornando a função wrapper
que envolve a função de entrada.
Podemos usar esse decorador personalizado da seguinte forma:
@medir_tempo("Tempo de execução:")
def minha_funcao():
# código da função aqui
pass
minha_funcao()
Nesse exemplo, a função medir_tempo
é chamada com o argumento "Tempo de execução:"
, retornando o decorador personalizado que pode agora ser usado para decorar a função minha_funcao
. Quando a função minha_funcao
é executada, o decorador personalizado mede o tempo de execução e imprime a mensagem personalizada:
Tempo de execução: 0.02 segundos
Conclusão
Os decoradores, ou decorators são uma característica poderosa e flexível do Python que permitem a adição de funcionalidades a funções e classes existentes sem modificar seu código. Eles são particularmente úteis para tarefas como a validação de entrada, o cache de resultados, o registro de eventos, a autenticação de usuários e a medição de tempo de execução.
Neste artigo, exploramos os fundamentos dos decoradores em Python, incluindo a sintaxe básica, a aplicação a funções e classes, a criação de decoradores de classe e a personalização de decoradores com argumentos. Com essas ferramentas em mãos, você pode começar a criar seus próprios decoradores personalizados para simplificar e estender seu código Python.