Decoratoren in Python
Dit artikel legt decoratoren in Python uit.
YouTube Video
Decoratoren in Python
Python-decoratoren zijn een krachtige functie om extra functionaliteit toe te voegen aan functies of methoden. Decoratoren stellen u in staat nieuwe functionaliteit toe te voegen zonder de bestaande code te wijzigen, waardoor de herbruikbaarheid en onderhoudbaarheid van de code verbeteren.
Basisprincipes van Decoratoren
Python-decoratoren werken door een functie als argument te nemen en een nieuwe functie terug te geven met extra functionaliteit. Met decoratoren kunt u eenvoudig voorverwerking of naverwerking toevoegen aan functies.
Zonder decoratoren vereist het wijzigen van het gedrag van een functie directe bewerking van de functie zelf. Met decoratoren kunt u functionaliteit uitbreiden zonder de oorspronkelijke implementatie te wijzigen.
Basisstructuur van een Decorator
1def my_decorator(func):
2 def wrapper():
3 print("Before the function call")
4 func()
5 print("After the function call")
6 return wrapper
7
8@my_decorator
9def say_hello():
10 print("Hello!")
11
12say_hello()
In dit voorbeeld fungeert de functie my_decorator
als een decorator en omhult de functie say_hello
. De decorator wordt toegepast met behulp van de @my_decorator
-syntax, en wanneer say_hello()
wordt aangeroepen, wordt de voor- en naverwerking die door de decorator wordt geleverd automatisch uitgevoerd.
Hoe Decoratoren Werken
Decorators werken in de volgende stappen.
-
De decorator neemt een functie (of methode) als argument.
-
Hij definieert een wrapper-functie om de oorspronkelijke functie uit te voeren.
-
De wrapper-functie voert extra verwerking uit vóór of na het uitvoeren van de oorspronkelijke functie.
-
De decorator retourneert de wrapper-functie. Als resultaat wordt de oorspronkelijke functie vervangen door een nieuwe, verfraaide functie.
Decoratoren voor Functies met Argumenten
Bij het toepassen van een decorator op een functie met argumenten, moet de wrapper-functie worden aangepast om die argumenten te accepteren.
1def my_decorator(func):
2 def wrapper(*args, **kwargs):
3 print("Before the function call")
4 result = func(*args, **kwargs)
5 print("After the function call")
6 return result
7 return wrapper
8
9@my_decorator
10def greet(name):
11 print(f"Hello, {name}!")
12
13greet("Alice")
Door gebruik te maken van *args
en **kwargs
, is het mogelijk om functies te verwerken die een willekeurig aantal argumenten en sleutelwoordargumenten accepteren. Dit maakt het mogelijk om decoratoren generiek toe te passen op functies met elk type argumenten.
Voorbeelden van het Toepassen van Decorators
Decorator voor Logging
Decorators worden vaak gebruikt om logging-functionaliteit toe te voegen. Bijvoorbeeld, door een decorator te maken die logt vóór en na het uitvoeren van een functie, kun je bijhouden wanneer de functie wordt aangeroepen en hoe lang de uitvoering duurt.
1import time
2
3def log_time(func):
4 def wrapper(*args, **kwargs):
5 start_time = time.time()
6 print(f"Calling function '{func.__name__}'...")
7 result = func(*args, **kwargs)
8 end_time = time.time()
9 print(f"Function '{func.__name__}' completed in {end_time - start_time} seconds")
10 return result
11 return wrapper
12
13@log_time
14def long_task(duration):
15 time.sleep(duration)
16 print("Task completed!")
17
18long_task(2)
In dit voorbeeld meet de log_time
-decorator de uitvoeringstijd van een functie en registreert deze als een log. De long_task
-functie is omhuld met de decorator, en de uitvoeringstijd wordt tijdens runtime geregistreerd.
Decorator voor Machtigingenbeheer
Decorators kunnen ook worden gebruikt voor machtigingenbeheer. Bijvoorbeeld, je kunt verwerking beperken door te controleren of een gebruiker specifieke machtigingen heeft.
1def requires_permission(user_role):
2 def decorator(func):
3 def wrapper(*args, **kwargs):
4 if user_role != "admin":
5 print("Permission denied!")
6 return
7 return func(*args, **kwargs)
8 return wrapper
9 return decorator
10
11@requires_permission("admin")
12def delete_user(user_id):
13 print(f"User {user_id} has been deleted.")
14
15delete_user(123) # Executed
16delete_user = requires_permission("guest")(delete_user)
17delete_user(456) # Permission denied!
De requires_permission
-decorator beperkt de uitvoering van een functie op basis van de rol van een gebruiker. Door dergelijke logica te consolideren in een decorator, kun je voorkomen dat machtigingenbeheerlogica verspreid raakt in je code, wat de leesbaarheid vergroot.
Geneste Decorators
Het is mogelijk om meerdere decorators toe te passen. Wanneer meerdere decorators worden toegepast op één functie, worden ze uitgevoerd in de volgorde van boven naar beneden.
1def uppercase(func):
2 def wrapper(*args, **kwargs):
3 result = func(*args, **kwargs)
4 return result.upper()
5 return wrapper
6
7def exclaim(func):
8 def wrapper(*args, **kwargs):
9 result = func(*args, **kwargs)
10 return result + "!"
11 return wrapper
12
13@uppercase
14@exclaim
15def greet(name):
16 return f"Hello, {name}"
17
18print(greet("Alice")) # "HELLO, ALICE!"
In dit voorbeeld worden twee decorators toegepast op de greet
-functie. De @exclaim
-decorator voegt een uitroepteken toe aan het einde van de string, en vervolgens zet @uppercase
de string om naar hoofdletters.
Decorators voor Klassenmethoden
Decorators kunnen ook worden gebruikt met klassenmethoden. Dit is vooral handig wanneer je het gedrag van methoden binnen een klasse wilt beheersen. Bij het toepassen van decorators op klassenmethoden moet je rekening houden met argumenten zoals self
of cls
.
1def log_method_call(func):
2 def wrapper(self, *args, **kwargs):
3 print(f"Calling method '{func.__name__}'...")
4 return func(self, *args, **kwargs)
5 return wrapper
6
7class MyClass:
8 @log_method_call
9 def greet(self, name):
10 print(f"Hello, {name}")
11
12obj = MyClass()
13obj.greet("Bob")
In dit voorbeeld wordt de log_method_call
-decorator toegepast op de greet
-methode om een log weer te geven wanneer de methode wordt aangeroepen.
Voorzichtigheid bij Decorators
Er zijn enkele overwegingen bij het gebruik van decorators. Decorators kunnen mogelijk de naam en documentatiestring van de oorspronkelijke functie veranderen (bijv. __name__
of __doc__
), dus het wordt aanbevolen om functools.wraps
te gebruiken om deze informatie te behouden.
1import functools
2
3def my_decorator(func):
4 @functools.wraps(func)
5 def wrapper(*args, **kwargs):
6 print("Before the function call")
7 return func(*args, **kwargs)
8 return wrapper
Het gebruik van @functools.wraps
zorgt ervoor dat de metadata van de oorspronkelijke functie correct wordt doorgegeven aan de wrapper-functie.
Samenvatting
Python-decorators zijn een zeer krachtig hulpmiddel waarmee je op beknopte wijze extra functionaliteit kunt toevoegen aan functies en methoden. Ze kunnen in verschillende scenario's worden gebruikt om code-duplicatie te verminderen en de onderhoudbaarheid en herbruikbaarheid te verbeteren. Door te begrijpen hoe decorators werken, kun je efficiëntere en flexibelere code schrijven.
Je kunt het bovenstaande artikel volgen met Visual Studio Code op ons YouTube-kanaal. Bekijk ook het YouTube-kanaal.