Ga naar hoofdinhoud

Python-decorators gebruiken (met voorbeelden op basis van functies en klassen)

Leer Python-decorators met praktijkvoorbeelden. Begrijp closures, function- en class-based decorators en hoe je herbruikbare, elegante code schrijft.
Bijgewerkt 1 jun 2026  · 11 min lezen

Decorators zijn een krachtige en elegante feature in Python waarmee je het gedrag van functies en methoden kunt wijzigen of uitbreiden zonder hun code aan te passen.

Een decorator is een ontwerppatroon in Python waarmee je nieuwe functionaliteit kunt toevoegen aan een bestaand object zonder de structuur ervan te wijzigen. Decorators worden meestal toegepast op functies en spelen een cruciale rol bij het verbeteren of aanpassen van het gedrag van functies. Traditioneel plaats je decorators vóór de definitie van de functie die je wilt decoreren. In deze tutorial laten we zien hoe je decorators effectief inzet in Python-functies.

TL;DR

  • Met decorators kun je het gedrag van functies aanpassen of uitbreiden zonder de originele code te wijzigen
  • Gebruik de syntaxis @decorator_name om decorators netjes toe te passen
  • Gebruik altijd functools.wraps om functiemetadata te behouden wanneer je decorators schrijft
  • Class-based decorators maken stateful gedrag over functieroepen heen mogelijk
  • Veelvoorkomende use-cases zijn caching (@lru_cache), logging, authenticatie en inputvalidatie

Functies als first-class objects

Functies in Python zijn first-class citizens. Dit betekent dat ze bewerkingen ondersteunen zoals doorgeven als argument, retourneren vanuit een functie, wijzigen en toewijzen aan een variabele. Deze eigenschap is cruciaal omdat functies hierdoor als elk ander object in Python worden behandeld, wat meer flexibiliteit in programmeren mogelijk maakt.

Wil je alle voorbeeldcode in deze tutorial zelf eenvoudig uitvoeren? Je kunt gratis een DataLab-werkboek aanmaken met Python vooraf geïnstalleerd en alle codevoorbeelden erin. Voor extra oefening met decorators, bekijk deze praktische DataCamp-oefening.

Functies toewijzen aan variabelen

Om te beginnen maken we een functie die telkens één bij een getal optelt wanneer deze wordt aangeroepen. Vervolgens wijzen we de functie toe aan een variabele en gebruiken we die variabele om de functie aan te roepen.

def plus_one(number):
    return number + 1

add_one = plus_one
add_one(5)
6

Functies binnen andere functies definiëren 

Functies binnen andere functies definiëren is een krachtige feature in Python—en essentieel voor het bouwen van decorators. Laten we naar een ander kernidee kijken: functies doorgeven als argumenten. Dit brengt ons een stap dichter bij het schrijven van decorators.

def plus_one(number):
    def add_one(number):
        return number + 1


    result = add_one(number)
    return result
plus_one(4)
5

Functies doorgeven als argumenten aan andere functies

Functies kunnen ook als parameter aan andere functies worden doorgegeven. Hieronder illustreren we dat.

def plus_one(number):
    return number + 1

def function_call(function):
    number_to_add = 5
    return function(number_to_add)

function_call(plus_one)
6

Functies die andere functies retourneren

Een functie kan ook een andere functie genereren. We laten dat hieronder zien met een voorbeeld.

def hello_function():
    def say_hi():
        return "Hi"
    return say_hi
hello = hello_function()
hello()
'Hi'

Inner functions en closures

Python staat toe dat een geneste functie toegang heeft tot de outer scope van de omvattende functie. Dit is een cruciaal concept bij decorators en staat bekend als een closure.

Een closure in Python is een functie die de omgeving waarin zij is gecreëerd onthoudt, zelfs nadat die omgeving niet meer actief is. Dit betekent dat een geneste functie variabelen uit de omvattende scope kan “vastleggen” en blijven gebruiken.

Closures zijn essentieel om decorators te begrijpen, omdat decorators vertrouwen op het vermogen van een geneste wrapperfunctie om de staat van de omvattende decoratorfunctie te benaderen en te wijzigen.

Voorbeeld van een closure:

def outer_function(message):
    def inner_function():
        print(f"Message from closure: {message}")
    return inner_function

closure_function = outer_function("Hello, closures!")
closure_function()
# Output: Message from closure: Hello, closures!

In dit voorbeeld:

  • inner_function is een closure omdat het message benadert, een variabele uit de omvattende scope (outer_function).
  • Ook al is outer_function klaar met uitvoeren, inner_function behoudt toegang tot message.

Wanneer je een decorator maakt, is de wrapperfunctie (binnen de decorator) een closure. Deze behoudt toegang tot de te decoreren functie en eventuele extra staat of argumenten die in de decoratorfunctie zijn gedefinieerd. Bijvoorbeeld:

def simple_decorator(func):
    def wrapper():
        print("Before the function call")
        func()
        print("After the function call")
    return wrapper

@simple_decorator
def greet():
    print("Hello!")

greet()
# Output:
# Before the function call
# Hello!
# After the function call

Hier is wrapper een closure die de functie greet onthoudt en gedrag toevoegt vóór en na de uitvoering ervan.

Je eerste decorator maken

Nu je closures begrijpt—het vermogen van een functie om variabelen uit de omvattende scope te onthouden—kunnen we onze eerste echte decorator maken. Closures zijn het geheime ingrediënt waardoor decorators achter de schermen werken.

Laten we een eenvoudige decorator maken die een zin omzet naar hoofdletters. We doen dit door een wrapper binnen een omvattende functie te definiëren. Zoals je ziet, lijkt dit sterk op de functie-in-een-functie die we eerder maakten.

def uppercase_decorator(function):
    def wrapper():
        func = function()
        make_uppercase = func.upper()
        return make_uppercase

    return wrapper

Omdat onze decorator een functie als argument neemt, definiëren we een nieuwe functie en geven die door aan de decorator. We leerden eerder dat we een functie aan een variabele kunnen toewijzen. Die truc gebruiken we om onze decoratorfunctie aan te roepen.

def say_hi():
    return 'hello there'

decorate = uppercase_decorator(say_hi)
decorate()
'HELLO THERE'

De @-syntaxis gebruiken

Python biedt echter een veel eenvoudigere manier om decorators toe te passen. We gebruiken simpelweg het @-symbool vóór de functie die we willen decoreren. Hieronder laten we dat in de praktijk zien.

@uppercase_decorator
def say_hi():
    return 'hello there'

say_hi()
'HELLO THERE'

Meerdere decorators stapelen

Als je de @-syntaxis voor één decorator onder de knie hebt, kun je een stap verder gaan en meerdere decorators op dezelfde functie stapelen. Let wel: de volgorde is belangrijk! 

Hieronder definiëren we nog een decorator die de zin opsplitst in een lijst. Vervolgens passen we de decorators uppercase_decorator en split_string toe op één functie.

import functools
def split_string(function):
    @functools.wraps(function)
    def wrapper():
        func = function()
        splitted_string = func.split()
        return splitted_string

    return wrapper 
@split_string
@uppercase_decorator
def say_hi():
    return 'hello there'
say_hi()
['HELLO', 'THERE']

Uit de uitvoer hierboven zien we dat de toepassing van decorators van onder naar boven gebeurt. Hadden we de volgorde omgewisseld, dan zouden we een fout zien, omdat lijsten geen upper-attribuut hebben. De zin is eerst naar hoofdletters omgezet en daarna opgesplitst in een lijst.

Let op: Bij het stapelen van decorators is het gebruik van functools.wraps gangbaar om ervoor te zorgen dat de metadata van de oorspronkelijke functie behouden blijft tijdens het stapelproces. Dit helpt bij duidelijkheid en consistentie tijdens het debuggen en het begrijpen van de eigenschappen van de gedecoreerde functie.

Argumenten accepteren in decorators

Tot nu toe hebben we decorators gezien die alleen een functie inpakken. Maar wat als je de decorator zelf wilt configureren—bijvoorbeeld er parameters aan doorgeven? Dan komen decorator factories in beeld.

def decorator_with_arguments(function):
    def wrapper_accepting_arguments(arg1, arg2):
        print("My arguments are: {0}, {1}".format(arg1,arg2))
        function(arg1, arg2)
    return wrapper_accepting_arguments


@decorator_with_arguments
def cities(city_one, city_two):
    print("Cities I love are {0} and {1}".format(city_one, city_two))

cities("Nairobi", "Accra")

My arguments are: Nairobi, Accra Cities I love are Nairobi and Accra

Let op: Het is essentieel dat het aantal argumenten in de decorator (
arg1, arg2 in dit voorbeeld) overeenkomt met het aantal argumenten in de omwikkelde functie (cities in dit voorbeeld). Deze afstemming is cruciaal om fouten te voorkomen en een correcte werking te garanderen wanneer je decorators met argumenten gebruikt.

Algemene decorators met *args en **kwargs

Om een algemene decorator te definiëren die op elke functie kan worden toegepast, gebruiken we *args en **kwargs. *args en **kwargs verzamelen alle positionele en keyword-argumenten en slaan ze op in de variabelen *args en **kwargs. Met *args en **kwargs kunnen we zoveel argumenten doorgeven als we willen bij functieroepen.

def a_decorator_passing_arbitrary_arguments(function_to_decorate):
    def a_wrapper_accepting_arbitrary_arguments(*args,**kwargs):
        print('The positional arguments are', args)
        print('The keyword arguments are', kwargs)
        function_to_decorate(*args)
    return a_wrapper_accepting_arbitrary_arguments

@a_decorator_passing_arbitrary_arguments
def function_with_no_argument():
    print("No arguments here.")

function_with_no_argument()
The positional arguments are ()
The keyword arguments are {}
No arguments here.

Laten we zien hoe we de decorator met positionele argumenten gebruiken.

@a_decorator_passing_arbitrary_arguments
def function_with_arguments(a, b, c):
    print(a, b, c)

function_with_arguments(1,2,3)
The positional arguments are (1, 2, 3)
The keyword arguments are {}
1 2 3

Zo geef je keyword-argumenten door aan een gedecoreerde functie:

@a_decorator_passing_arbitrary_arguments
def function_with_keyword_arguments():
    print("This has shown keyword arguments")

function_with_keyword_arguments(first_name="Derrick", last_name="Mwiti")
The positional arguments are ()
The keyword arguments are {'first_name': 'Derrick', 'last_name': 'Mwiti'}
This has shown keyword arguments

Let op: Het gebruik van **kwargs in de decorator maakt het mogelijk om keyword-argumenten af te handelen. Dit maakt de algemene decorator veelzijdig en in staat om verschillende argumenttypen bij functieroepen te verwerken.

Argumenten doorgeven aan de decorator

Nu bekijken we hoe we argumenten doorgeven aan de decorator zelf. Om dit te bereiken, definiëren we een decorator-maker die argumenten accepteert en definiëren we daarin een decorator. Vervolgens definiëren we binnen de decorator een wrapperfunctie, zoals we eerder deden.

def decorator_maker_with_arguments(decorator_arg1, decorator_arg2, decorator_arg3):
    def decorator(func):
        def wrapper(function_arg1, function_arg2, function_arg3) :
            "This is the wrapper function"
            print("The wrapper can access all the variables\n"
                  "\t- from the decorator maker: {0} {1} {2}\n"
                  "\t- from the function call: {3} {4} {5}\n"
                  "and pass them to the decorated function"
                  .format(decorator_arg1, decorator_arg2,decorator_arg3,
                          function_arg1, function_arg2,function_arg3))
            return func(function_arg1, function_arg2,function_arg3)

        return wrapper

    return decorator

pandas = "Pandas"
@decorator_maker_with_arguments(pandas, "Numpy","Scikit-learn")
def decorated_function_with_arguments(function_arg1, function_arg2,function_arg3):
    print("This is the decorated function and it only knows about its arguments: {0}"
           " {1}" " {2}".format(function_arg1, function_arg2,function_arg3))

decorated_function_with_arguments(pandas, "Science", "Tools")
The wrapper can access all the variables
    - from the decorator maker: Pandas Numpy Scikit-learn
    - from the function call: Pandas Science Tools
and pass them to the decorated function
This is the decorated function, and it only knows about its arguments: Pandas Science Tools

Decorators debuggen

Zoals we hebben gezien, wikkelen decorators functies in. De oorspronkelijke functienaam, docstring en parameterlijst worden verborgen door de wrapper-closure: wanneer we bijvoorbeeld de metadata van decorated_function_with_arguments proberen te benaderen, zien we de metadata van de wrapper-closure. Dit is een uitdaging bij het debuggen.

decorated_function_with_arguments.__name__
'wrapper'
decorated_function_with_arguments.__doc__
'This is the wrapper function'

Om dit op te lossen biedt Python de functools.wraps-decorator. Deze decorator kopieert de verloren metadata van de ongedecoreerde functie naar de gedecoreerde closure. Zo doen we dat:

import functools

def uppercase_decorator(func):
    @functools.wraps(func)
    def wrapper():
        return func().upper()
    return wrapper
@uppercase_decorator
def say_hi():
    "This will say hi"
    return 'hello there'

say_hi()
'HELLO THERE'

Als we nu de metadata van say_hi bekijken, zien we dat deze correct de oorspronkelijke functie weergeeft—niet de wrapper.

say_hi.__name__
'say_hi'
say_hi.__doc__
'This will say hi'

Het is aan te raden en goede praktijk om altijd functools.wraps te gebruiken bij het definiëren van decorators. Het bespaart je een hoop hoofdpijn tijdens het debuggen.

Class-based decorators

Hoewel function-based decorators gebruikelijk zijn, kun je in Python ook class-based decorators maken, die meer flexibiliteit en onderhoudbaarheid bieden—vooral bij complexe use-cases. Als objectgeoriënteerd programmeren in Python nieuw voor je is, helpt een korte opfrissing om dit gedeelte beter te begrijpen. Een class-based decorator is een klasse met een __call__-methode, waardoor hij zich als een functie gedraagt.

class UppercaseDecorator:
    def __init__(self, function):
        self.function = function

    def __call__(self, *args, **kwargs):
        result = self.function(*args, **kwargs)
        return result.upper()

@UppercaseDecorator
def greet():
    return "hello there"

print(greet())
# Output: HELLO THERE

Hoe het werkt:

  1. De __init__-methode initialiseert de decorator met de functie die gedecoreerd moet worden.
  2. De __call__-methode wordt aangeroepen wanneer de gedecoreerde functie wordt aangeroepen, waardoor de decorator het gedrag kan wijzigen.

Voordelen van class-based decorators:

  • Stateful decorators: Class-based decorators kunnen state bewaren met instantievariabelen, in tegenstelling tot function-based decorators die closures of globale variabelen nodig hebben.
  • Leesbaarheid: Voor complexe decorators kan het encapsuleren van logica in een klasse de code overzichtelijker en begrijpelijker maken.

Voorbeeld van een stateful decorator:

class CallCounter:
    def __init__(self, function):
        self.function = function
        self.count = 0

    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f"Function {self.function.__name__} has been called {self.count} times.")
        return self.function(*args, **kwargs)

@CallCounter
def say_hello():
    print("Hello!")

say_hello()
say_hello()
# Output:
# Function say_hello has been called 1 times.
# Hello!
# Function say_hello has been called 2 times.
# Hello!

Decorator in de praktijk: caching

De lru_cache-decorator is een ingebouwd hulpmiddel in Python dat de resultaten van dure functieroepen cachet. Dit verbetert de performance door dubbele berekeningen voor herhaalde invoer te vermijden.

Voorbeeld:

from functools import lru_cache

@lru_cache(maxsize=128)
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

print(fibonacci(50))  # Subsequent calls with the same argument are much faster

Andere veelvoorkomende toepassingen voor decorators:

  • Logging: Houd functieroepen, argumenten en returnwaarden bij voor debugging of auditing.

  • Authenticatie: Handhaaf toegangscontrole in webapplicaties zoals Flask of Django.

  • Uitvoeringstijd meten: Meet en optimaliseer de uitvoeringstijd van functies voor performancekritische taken.

  • Retry-mechanisme: Probeert mislukte functieroepen automatisch opnieuw—handig bij netwerkoperaties.

  • Inputvalidatie: Valideer functie-argumenten vóór uitvoering.

Samenvatting van Python-decorators

Decorators wijzigen dynamisch de functionaliteit van een functie, methode of klasse zonder direct subklassen te gebruiken of de broncode van de gedecoreerde functie aan te passen. Door decorators in Python te gebruiken, blijft je code ook DRY (Don't Repeat Yourself). Decorators hebben verschillende use-cases, zoals:

  • Autorisatie in Python-frameworks zoals Flask en Django
  • Logging
  • Uitvoeringstijd meten
  • Synchronisatie

Wil je meer leren over Python-decorators? Bekijk de Decorator Library van Python.

FAQs

Zijn er prestatieoverwegingen bij het gebruik van decorators?

Ja, decorators kunnen extra overhead toevoegen omdat ze extra functieroepen introduceren. Als performance cruciaal is, is het belangrijk om met deze overhead rekening te houden, vooral wanneer de gedecoreerde functie vaak wordt aangeroepen in een performancegevoelige context.

Kunnen decorators worden gebruikt met class-methoden, en zo ja, hoe?

Ja, decorators kunnen net als bij gewone functies op methoden van klassen worden toegepast. De decorator ontvangt de methode als argument en retourneert een nieuwe of aangepaste versie van de methode. Dit wordt vaak gebruikt voor logging, toegangscontrole of het afdwingen van precondities.

Hoe kunnen decorators worden gebruikt voor logging?

Decorators kunnen worden gebruikt om functieroepen, hun argumenten en returnwaarden te loggen door de functie-uitvoering te omwikkelen met code die deze details naar een loggingsysteem wegschrijft. Dit helpt bij traceren en debuggen.

Wat is de betekenis van het @-symbool in decorators?

Het @ symbool is syntactische suiker in Python die het toepassen van een decorator op een functie vereenvoudigt. Je kunt zo direct boven de functiedefinitie een decorator toepassen, wat de code schoner en leesbaarder maakt.

Kan een decorator de returnwaarde van een functie aanpassen, en hoe werkt dat?

Ja, een decorator kan de returnwaarde van een functie aanpassen door de return in de wrapperfunctie te wijzigen. Zo kan hij bijvoorbeeld het uitvoertype transformeren, het formatteren of extra verwerking toevoegen voordat het resultaat wordt geretourneerd.

Hoe gaat Python om met variabelescope wanneer een geneste functie een variabele uit de omvattende functie benadert?

Python hanteert de LEGB-regel (Local, Enclosing, Global, Built-in). Bij geneste functies kan de geneste functie variabelen uit de scope van de omvattende functie benaderen. Dit maakt closures mogelijk, waarbij de innerlijke functie toegang behoudt tot de variabelen van de buitenste functie, zelfs nadat die is voltooid.

Wat is het verschil tussen een closure en een decorator?

Een closure is een functie die variabelen uit zijn outer scope onthoudt. Een decorator is een functie die closures gebruikt om andere functies uit te breiden of in te wikkelen.

Wanneer moet ik functools.wraps gebruiken?

Gebruik altijd functools.wraps wanneer je decorators schrijft. Het behoudt de metadata van de originele functie (zoals naam en docstring), wat handig is voor debugging en documentatie.

Kunnen decorators argumenten accepteren?

Ja! Je kunt argumenten aan decorators doorgeven door ze te omwikkelen met een extra functie (een decorator factory). Hiermee kun je het gedrag van de decorator aanpassen.

Wat is het voordeel van class-based decorators?

Class-based decorators laten je state behouden over meerdere functieroepen en maken het makkelijker om complexere logica op een objectgeoriënteerde manier te organiseren.

Zijn decorators alleen voor functies?

Nee. Je kunt decorators ook gebruiken op klassen en methoden. In frameworks zoals Flask en Django worden decorators vaak toegepast op routes, views en models.

Onderwerpen

Leer meer over Python

Cursus

Introductie tot functies in Python

3 Hr
466.3K
Leer de kunst van het schrijven van je eigen functies in Python, evenals belangrijke concepten zoals scoping en foutafhandeling.
Bekijk detailsRight Arrow
Begin met de cursus
Meer zienRight Arrow
Gerelateerd

blog

AI vanaf nul leren in 2026: een complete gids van de experts

Ontdek alles wat je moet weten om in 2026 AI te leren, van tips om te beginnen tot handige resources en inzichten van industrie-experts.
Adel Nehme's photo

Adel Nehme

15 min

Meer zienMeer zien