Criando Decoradores com Argumentos
Até agora, temos usado o decorator @logged, que sempre imprime uma mensagem fixa. Mas e se você quiser personalizar o formato da mensagem? Nesta seção, aprenderemos como criar um novo decorator que pode aceitar argumentos, dando a você mais flexibilidade na forma como você usa decorators.
Entendendo Decoradores Parametrizados
Um decorator parametrizado é um tipo especial de função. Em vez de modificar diretamente outra função, ele retorna um decorator. A estrutura geral de um decorator parametrizado se parece com isto:
def decorator_with_args(arg1, arg2, ...):
def actual_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
## Use arg1, arg2, ... here
## Call the original function
return func(*args, **kwargs)
return wrapper
return actual_decorator
Quando você usa @decorator_with_args(value1, value2) em seu código, o Python primeiro chama decorator_with_args(value1, value2). Essa chamada retorna o decorator real, que é então aplicado à função que segue a sintaxe @. Esse processo de duas etapas é fundamental para o funcionamento dos decorators parametrizados.
Vamos criar um decorator @logformat(fmt) que recebe uma string de formato como argumento. Isso nos permitirá personalizar a mensagem de logging.
- Abra
logcall.py no WebIDE e adicione o novo decorator. O código abaixo mostra como definir tanto o decorator logged existente quanto o novo decorator logformat:
from functools import wraps
def logged(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__}")
return func(*args, **kwargs)
return wrapper
def logformat(fmt):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(fmt.format(func=func))
return func(*args, **kwargs)
return wrapper
return decorator
No decorator logformat, a função externa logformat recebe uma string de formato fmt como argumento. Em seguida, ele retorna a função decorator, que é o decorator real que modifica a função de destino.
- Agora, vamos testar nosso novo decorator modificando
sample.py. O código a seguir mostra como usar os decorators logged e logformat em diferentes funções:
from logcall import logged, logformat
@logged
def add(x, y):
"Adds two numbers"
return x + y
@logged
def sub(x, y):
"Subtracts y from x"
return x - y
@logformat('{func.__code__.co_filename}:{func.__name__}')
def mul(x, y):
"Multiplies two numbers"
return x * y
Aqui, as funções add e sub usam o decorator logged, enquanto a função mul usa o decorator logformat com uma string de formato personalizada.
- Execute o
sample.py atualizado para ver os resultados. Abra seu terminal e execute o seguinte comando:
cd ~/project
python3 -c "import sample; print(sample.add(2, 3)); print(sample.mul(2, 3))"
Você deve ver uma saída semelhante a:
Calling add
5
sample.py:mul
6
Essa saída mostra que o decorator logged imprime o nome da função como esperado, e o decorator logformat usa a string de formato personalizada para imprimir o nome do arquivo e o nome da função.
Agora que temos um decorator logformat mais flexível, podemos redefinir nosso decorator logged original usando-o. Isso nos ajudará a reutilizar o código e manter um formato de logging consistente.
- Atualize
logcall.py com o seguinte código:
from functools import wraps
def logformat(fmt):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(fmt.format(func=func))
return func(*args, **kwargs)
return wrapper
return decorator
## Define logged using logformat
logged = lambda func: logformat("Calling {func.__name__}")(func)
Aqui, usamos uma função lambda para definir o decorator logged em termos do decorator logformat. A função lambda recebe uma função func e aplica o decorator logformat com uma string de formato específica.
- Teste se o decorator
logged redefinido ainda funciona. Abra seu terminal e execute o seguinte comando:
cd ~/project
python3 -c "from logcall import logged; @logged
def greet(name):
return f'Hello, {name}'
print(greet('World'))"
Você deve ver:
Calling greet
Hello, World
Isso mostra que o decorator logged redefinido funciona como esperado, e reutilizamos com sucesso o decorator logformat para obter um formato de logging consistente.