2.23. Decoradores¶
Um decorador é uma construção de sintaxe que envolve uma função (ou um método) em outra função. O invólucro pode adicionar comportamento antes e depois da chamada original, substituir o valor de retorno ou anexar metadados. O formato é:
@wrapper
def f():
...
A linha @wrapper é equivalente a f = wrapper(f) – a função f é construída normalmente, depois entregue a wrapper, e o resultado é revinculado ao nome f.
Um decorador recebe uma função e retorna uma nova função.¶
2.23.1. Decoradores de método embutidos¶
Alguns decoradores vêm com o Python e são usados dentro de corpos de classe.
2.23.1.1. @property¶
Transforma um método em um atributo computado. O chamador o acessa como se fosse um atributo comum (sem parênteses), mas um método é executado a cada leitura:
class Circle:
def __init__(self, radius):
self.radius = radius
@property
def area(self):
return 3.14159 * self.radius * self.radius
c = Circle(5)
print(c.area) # 78.53975 -- no parentheses
Use-o quando um atributo que parece simples precisa de um pequeno cálculo por trás. Se o trabalho for caro, prefira um método comum – os chamadores não esperam que leituras de atributos sejam lentas.
2.23.1.2. @classmethod¶
Define um método que recebe a classe como primeiro argumento em vez de uma instância. O primeiro parâmetro é convencionalmente chamado de cls:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
@classmethod
def origin(cls):
return cls(0, 0)
p = Point.origin()
Métodos de classe são a forma padrão de fornecer construtores alternativos – funções de fábrica que retornam uma instância construída de uma maneira não padrão.
2.23.1.3. @staticmethod¶
Define um método que não recebe nem a instância nem a classe – é apenas uma função comum que vive no namespace da classe por motivos de organização:
class Temperature:
@staticmethod
def c_to_f(c):
return c * 9 / 5 + 32
Temperature.c_to_f(100) # 212.0
Use-o com moderação; se uma função realmente não tem nada a ver com o estado da classe, uma função comum em nível de módulo costuma ser mais limpa.
2.23.2. Escrevendo um decorador personalizado¶
Um decorador é uma função que recebe uma função e retorna uma função. O formato mínimo:
def log_calls(func):
def wrapper(*args, **kwargs):
print("calling", func.__name__)
return func(*args, **kwargs)
return wrapper
@log_calls
def add(a, b):
return a + b
add(2, 3)
Saída:
calling add
O wrapper faz closure sobre func e encaminha tudo para ele. *args / **kwargs permite que ele funcione com qualquer função, não apenas com aquelas de dois argumentos. Esse padrão é a base de decoradores mais elaborados (medição de tempo, cache, nova tentativa em caso de falha), mas o núcleo é sempre o mesmo: receber uma função, retornar uma função.