In Python is een hoge orde functie decorator een functie die een andere functie (of klasse) accepteert en een nieuwe functie (of een nieuw gemodificeerde klasse) retourneert. Decorators worden vaak gebruikt om het 'wrapping' patroon te implementeren, waarmee je extra logica (bijvoorbeeld logging, caching, rechtencontrole, enz.) aan bestaande functies kunt toevoegen zonder de oorspronkelijke code te wijzigen.
Om de naam, documentatie en andere metadata van de originele functie te behouden, is het aan te raden om de functie functools.wraps te gebruiken:
import functools def log_decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): print(f'Calling {func.__name__}') return func(*args, **kwargs) return wrapper @log_decorator def add(a, b): """Voeg twee getallen toe""" return a + b print(add(2, 3)) # Output: Calling add 5 print(add.__name__) # Output: add print(add.__doc__) # Output: Voeg twee getallen toe
Belangrijk punt: zonder functools.wraps verliest de gewrapte functie haar naam, documentatie en andere metadata van de originele functie, wat de debugging en autodocumentatie negatief beïnvloedt.
Als je een functie decoreert zonder functools.wraps, wat gebeurt er dan met de attributen name en doc van de functie?
Antwoord: Ze worden geërfd van de interne wrapperfunctie (meestal 'wrapper'), en je verliest de originele metadata.
def simple_decorator(func): def wrapper(*args, **kwargs): return func(*args, **kwargs) return wrapper @simple_decorator def f(): """Dit is een docstring""" pass print(f.__name__) # Output: 'wrapper' (NIET 'f') print(f.__doc__) # Output: None (en niet 'Dit is een docstring')
Verhaal
In een project implementeerden ze een geavanceerd systeem van decorators voor logging van API-endpoints, maar gebruikten ze geen functools.wraps. Als gevolg hiervan toonden de autodocumentatie-engine (Swagger/OpenAPI) en introspectiehulpmiddelen de namen van alle endpoints als 'wrapper', en verdween de documentatie. Dit bemoeilijkte ondersteuning, testen en onderhoud aanzienlijk.
Verhaal
Bij het schrijven van unittests met pytest trad er een fout op bij de auto-discovery van tests: testfuncties, gedecoreerd met hun decorators zonder wraps, werden niet gevonden, omdat hun name onjuist was. De reden — pytest zoekt functies op naam.
Verhaal
Bij het traceren van de stack van uitzonderingen wees de stack-trace altijd naar "wrapper", en het was onmogelijk om te begrijpen welke specifieke functie de fout veroorzaakte, aangezien de rootmetadata verloren gingen door het ontbreken van functools.wraps in de gebruikersdecorators.