ProgrammatiePython Backend ontwikkelaar

Wat zijn decorateurs voor functies in Python, wat is hun geschiedenis en waarom worden ze vandaag de dag in de programmering gebruikt?

Slaag voor sollicitatiegesprekken met de Hintsage AI-assistent

Antwoord

Geschiedenis van de kwestie

Decorateurs verschenen in Python als syntactische suiker met versie 2.4, om het werken met functies van hogere orde te vergemakkelijken - dat wil zeggen functies die andere functies aannemen of retourneren. De evolutie van benaderingen om de functionaliteit van functies uit te breiden leidde tot het formaat van beknopte en expressieve middelen - annotaties via @decorator.

Probleem

In grote projecten is het vaak nodig om functies te modificeren of te omhullen met extra logica: logging, toegangscontrole, caching, het meten van de uitvoeringstijd. Zonder decorateurs moest men handmatig omhulde functies aanroepen, wat de code opblies.

Oplossing

Decorateurs stellen je in staat om herhaaldelijk voorkomende aspecten naar aparte omhulsels te verplaatsen, waardoor de leesbaarheid en herbruikbaarheid van de code toeneemt. Met behulp van @decorator kan elegant functionaliteit aan functies en methoden worden toegevoegd:

Voorbeeldcode:

import time def timing_decorator(func): def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) print(f'Tijd verstreken: {time.time() - start:.3f}s') return result return wrapper @timing_decorator def langzame_functie(): time.sleep(0.5) langzame_functie() # Zal weergeven hoe lang de uitvoering duurde

Belangrijkste kenmerken:

  • Stellen je in staat om code te hergebruiken (DRY-principe);
  • Kunnen worden gebruikt voor functies, methoden, klassen;
  • Stellen je in staat om cross-functionele taken (logging, caching, toegang, profilering) centraal in te voegen;

Misleidende vragen.

Kunnen decorateurs de handtekening van de omhulde functie wijzigen?

Vaak wordt ten onrechte aangenomen dat de handtekening van de functie door een decorateur niet verandert. In werkelijkheid verdwijnt de metainformatie zonder het gebruik van de functools.wraps module, wat onverwachte fouten kan veroorzaken in autodocumentatie of introspectie.

Voorbeeldcode:

from functools import wraps def decorator(func): @wraps(func) def wrapper(*args): return func(*args) return wrapper

Kunnen decorateurs geparameteriseerd worden?

Vaak zeggen mensen dat dit niet kan. In feite is het mogelijk om een geparameteriseerde decorateur te maken via een extra niveau van genesting - een functie die een decorateur retourneert.

Voorbeeldcode:

def herhaal(n): def decorator(func): def wrapper(*args, **kwargs): result = None for _ in range(n): result = func(*args, **kwargs) return result return wrapper return decorator @herhaal(3) def hallo(): print("Hallo!")

Kan je meerdere decorateurs op één functie toepassen? Wat zal de volgorde van uitvoering zijn?

Vaak wordt ten onrechte gedacht dat de volgorde niet belangrijk is of dat deze samenvalt met de volgorde waarin de decorateurs in de code staan. In werkelijkheid wordt eerst de onderste decorateur toegepast, dan de volgende, en zo verder omhoog.

Voorbeeldcode:

def dec1(f): def wrapper(*a, **k): print("dec1") return f(*a, **k) return wrapper def dec2(f): def wrapper(*a, **k): print("dec2") return f(*a, **k) return wrapper @dec1 @dec2 def f(): print("core") f() # dec1, dec2, core

Typische fouten en antipatterns

  • Het negeren van functools.wraps verliest metainformatie over de originele functie;
  • De logica van de decorateur houdt geen rekening met uitzonderingen; je kunt een fout binnenin niet opvangen of verwerken;
  • Onopvallende overlap van meerdere decorateurs

Voorbeeld uit het leven

Negatieve case

Logging van de uitvoeringstijd is handmatig toegevoegd aan 10 functies door middel van kopiëren en plakken.

Voordelen:

  • Logica is duidelijk, de code is dichtbij, gemakkelijk om de fout te vinden.

Nadelen:

  • Moeilijk te onderhouden - er zullen tientallen plaatsen moeten worden gewijzigd als het gedrag moet worden aangepast.
  • Code is gedupliceerd, wat het DRY-principe schendt.

Positieve case

Alle logica voor timing is in een decorateur geplaatst, alle functies waar deze metric nodig is, zijn omhuld met de decorateur @timing_decorator.

Voordelen:

  • Wijzigingen worden centraal doorgevoerd;
  • Code is korter en leesbaarder.

Nadelen:

  • Mogelijk verlies van handtekeninginformatie zonder functools.wraps, als je niet voorzichtig bent;
  • Voor beginners is het moeilijker om meteen de werking van decorateurs te begrijpen.