Dekoratory to funkcje, które przyjmują inną funkcję i zwracają nową funkcję o rozszerzonym lub zmodyfikowanym zachowaniu. Są one często używane do logowania, sprawdzania uprawnień, buforowania, pomiaru czasu wykonania i innych.
Dekoratory są realizowane za pomocą zamknięć (closure) lub klas implementujących metodę __call__. Klasyczna składnia:
# Prosty dekorator def simple_decorator(func): def wrapper(*args, **kwargs): print("Przed wywołaniem funkcji") result = func(*args, **kwargs) print("Po wywołaniu funkcji") return result return wrapper @simple_decorator def my_func(): print("Podstawowa funkcja") my_func()
W rezultacie wykona się tak:
Przed wywołaniem funkcji
Podstawowa funkcja
Po wywołaniu funkcji
Główna użyteczność — enkapsulacja powtarzającej się logiki poza główną logiką biznesową.
Niuanse:
functools.wraps traci się nazwę pierwotnej funkcji i jej docstring.Pytanie: "Jeśli udekoruję funkcję dekoratorem, a następnie spróbuję uzyskać nazwę funkcji przez __name__, co zobaczę i jak zachować oryginalną nazwę?"
Odpowiedź:
Domyślnie nazwa zmieni się na nazwę owijki (zwykle wrapper). Aby zachować oryginalne metadane, użyj functools.wraps:
import functools def dec(f): @functools.wraps(f) def wrapper(*args): return f(*args) return wrapper @dec def foo(): pass print(foo.__name__) # wypisze 'foo', a nie 'wrapper'
Historia
W dużej automatyzacji zasobów, po owinięciu funkcji dekoratorami, system automatycznych testów oparty na refleksji przez nazwy funkcji testowych przestał działać. Problem tkwił w braku functools.wraps.
Historia
Dekorator, który dodaje logowanie, nie obsługiwał funkcji o różnych sygnaturach, ponieważ nie użyto *args, **kwargs. Niektóre funkcje po prostu przestały działać Silent Fail.
Historia
W projekcie z autoryzacją w REST API programista zaimplementował dekorator z parametrami, ale zapomniał poprawnie zagnieździć funkcje (było zagnieżdżenie dwupoziomowe zamiast trzech). W rezultacie dekorator nie mógł przyjmować parametrów.