Decorateurs in Python zijn historisch ontstaan als een manier om code compacter en leesbaarder te maken door herhalend gedrag rond functies en methoden in te kapselen. Voor de introductie van de @decorator syntaxis werden ze expliciet toegepast, wat het begrip van de code bemoeilijkte. Tegenwoordig spelen decorateurs een sleutelrol in de organisatie van logica, herhalende controles, logging, caching en meer.
Een probleem bij het werken met decorateurs is de juiste behandeling van de verschillen tussen functies, instantiemethoden en statische/klaar-methoden. Vaak ontstaan er fouten die verband houden met het verlies van informatie over de methode, late/vroege binding van self en de handtekening van de functie (signature).
De oplossing — bij het schrijven van algemene decorateurs moet de functools.wraps module worden gebruikt om metadata te behouden, en moet zorgvuldig worden omgegaan met het type van het gedecoreerde object (bijvoorbeeld, het correct rekening houden met het feit dat instantiemethoden self als het eerste argument ontvangen).
Voorbeeldcode:
import functools def my_decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): print(f"Voor functie: {func.__name__}") result = func(*args, **kwargs) print(f"Na functie: {func.__name__}") return result return wrapper class Voorbeeld: @my_decorator def methode(self, x): print(f"Methode aangeroepen met {x}") ex = Voorbeeld() ex.methode(5)
Belangrijke kenmerken:
functools.wraps te gebruiken.Als je een decorateur die voor een functie is geschreven op een klasse-methode toepast, is het dan verplicht om functools.wraps te gebruiken, en wat gebeurt er met self?
Nee, de decorateur werkt op zichzelf, maar zonder wraps gaat de naam van de functie, IDE-ondersteuning en documentatie verloren. Self blijft echter de eerste parameter, maar het verlies van metadata bemoeilijkt debugging en reflectie.
def bad_decorator(f): def wrapper(*args, **kwargs): print("gedecoreerd") return f(*args, **kwargs) return wrapper class Test: @bad_decorator def foo(self): pass print(Test().foo.__name__) # wrapper
Kan dezelfde decorateur zowel op een instantiemethode als op een statische methode worden toegepast?
Ja, maar het is belangrijk om te onthouden: statische methoden ontvangen niet self als eerste argument. Als de decorateur is ontworpen om met self te werken, zullen er fouten optreden met @staticmethod/@classmethod.
Hoe beïnvloedt de decorateur de handtekening van de methode en autocompletion?
Het eenvoudigste: zonder functools.wraps gaan de handtekening en docstring verloren, IDE’s en veel suggestiehulpmiddelen werken niet goed meer.
Negatieve case: Decorateurs zonder functools.wraps in alle methoden van de klasse.
Voordelen: snelle prototype, werkt.
Nadelen: onmogelijk om fouten in de stack te zoeken, IDE geeft geen handtekening aan.
Positieve case: Decorateurs gebruiken functools.wraps, code is gedocumenteerd.
Voordelen: leesbaarheid, onderhoud, IDE-comfort.
Nadelen: minimale toevoeging aan de syntaxis en aandacht.