2.23. Decoradores¶
Um decorador é uma peça de sintaxe que envolve uma função (ou um método) noutra função. O envólucro pode adicionar comportamento antes e depois da chamada original, substituir o valor de retorno ou anexar metadados. A forma é:
@wrapper
def f():
...
A linha @wrapper é equivalente a f = wrapper(f) – a função f é construída normalmente, depois passada ao wrapper, e o resultado é re-associado ao nome f.
Um decorador recebe uma função e devolve uma nova função.¶
2.23.1. Decoradores de métodos incorporados¶
Alguns decoradores são fornecidos com o Python e são usados dentro de corpos de classes.
2.23.1.1. @property¶
Transforma um método num atributo calculado. O invocador acede-lhe como se fosse um atributo simples (sem parênteses), mas um método é executado em 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 dispendioso, prefira um método regular – os invocadores 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 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()
Os métodos de classe são a forma padrão de fornecer construtores alternativos – funções de fábrica que devolvem uma instância construída de forma 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 simples que vive no espaço de nomes da classe por razões 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 parcimónia; se uma função realmente não tem nada a ver com o estado da classe, uma função simples ao nível do módulo é geralmente mais limpa.
2.23.2. Escrever um decorador personalizado¶
Um decorador é uma função que recebe uma função e devolve uma função. A forma mínima:
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)
Resultado:
calling add
O wrapper fecha sobre func e encaminha tudo para ele. *args / **kwargs permitem-lhe funcionar com qualquer função, não apenas com funções de dois argumentos. Este padrão é a base de decoradores mais elaborados (temporização, cache, retentar em caso de falha), mas o núcleo é sempre o mesmo: recebe uma função, devolve uma função.