Direkt zum Hauptinhalt springen
eLearner.app
Modul 9 · Lektion 4 von 436/36 im Kurs~14 min
Lektionen des Moduls (4/4)

Dekoratoren: Funktionen, die Funktionen modifizieren

Ein Dekorator (decorator) ist eine Funktion, die eine andere Funktion entgegennimmt und eine "dekorierte" Version davon zurückgibt. Es ist eine leistungsstarke Methode, um Verhalten (Logging, Zeitmessung, Caching, Retry, Validierung) hinzuzufügen, ohne den Rumpf der ursprünglichen Funktion zu verändern.

Funktionen als Werte

In Python sind Funktionen Objekte: Du kannst sie übergeben, zurückgeben und zuweisen.

Python
def saluta():
    print("ciao")

f = saluta   # NIENTE parentesi: assegno la funzione
f()          # 'ciao'

Ein minimaler Dekorator

Python
def log(func):
    def wrapper(*args, **kwargs):
        print(f"chiamo {func.__name__}")
        risultato = func(*args, **kwargs)
        print(f"ho finito {func.__name__}")
        return risultato
    return wrapper

def somma(a, b):
    return a + b

somma = log(somma)   # ora somma è "wrapper"
somma(2, 3)
# chiamo somma
# ho finito somma
# 5

Die @decorator-Syntax

Die Zuweisung somma = log(somma) wird mit @ eleganter geschrieben:

Python
@log
def somma(a, b):
    return a + b

Es ist genau dasselbe. @log über der Definition entspricht: "Sobald diese Funktion definiert ist, ersetze sie durch log(funktion)".

*args, **kwargs: Jede Signatur akzeptieren

Python
def wrapper(*args, **kwargs):
    return func(*args, **kwargs)

*args sammelt Positionsargumente, **kwargs Keyword-Argumente. Sie ermöglichen es dir, Wrapper zu schreiben, die mit jeder Funktion funktionieren.

functools.wraps: Metadaten beibehalten

Ohne wraps verliert die dekorierte Funktion __name__, __doc__ und Type-Hints:

Python
from functools import wraps

def log(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        ...
        return func(*args, **kwargs)
    return wrapper

Beispiel: @timeit

Python
from functools import wraps
import time

def timeit(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        t0 = time.perf_counter()
        risultato = func(*args, **kwargs)
        dt = time.perf_counter() - t0
        print(f"{func.__name__} ha impiegato {dt*1000:.2f} ms")
        return risultato
    return wrapper

@timeit
def somma_grande():
    return sum(range(10**6))

somma_grande()

Dekoratoren aus der stdlib

Python
from functools import lru_cache

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

fib(100)  # istantaneo grazie alla cache
  • @lru_cache – speichert die Ergebnisse einer reinen Funktion (Memoisation).
  • @property – macht eine Methode als Attribut zugänglich (siehe OOP-Modul).
  • @staticmethod, @classmethod – ändern die Art und Weise, wie self übergeben wird.

Dekoratoren mit Argumenten

Dekoratoren können auch Argumente entgegennehmen. Um dies zu implementieren, musst du drei Ebenen verschachtelter Funktionen schreiben: Die äußerste Funktion erhält die Argumente des Dekorators, die mittlere Funktion erhält die dekorierte Funktion und die innerste Funktion (wrapper) erhält die tatsächlichen Argumente, die der dekorierten Funktion beim Aufruf übergeben werden.

Probiere es aus

Übung#python.m9.l4.e1
Versuche: 0Wird geladen…

Schreibe einen Dekorator `double_result`, der die dekorierte Funktion aufruft und deren Ergebnis * 2 zurückgibt. Wende ihn auf eine Funktion `f(x)` an, die x + 1 zurückgibt. Evaluiere `f(10)`.

Editor wird geladen…
Hinweis anzeigen

Der Wrapper ruft func auf und gibt das mit 2 multiplizierte Ergebnis zurück.

Lösung nach 3 Versuchen verfügbar

Wiederholungsübung

Übung#python.m9.l4.e2
Versuche: 0Wird geladen…

Verwende functools.lru_cache, um eine rekursive fib(n)-Funktion zu beschleunigen. Berechne fib(30) und weise das Ergebnis `r` zu. Evaluiere `r`.

Editor wird geladen…
Hinweis anzeigen

@lru_cache(maxsize=None) über dem def.

Lösung nach 3 Versuchen verfügbar

Zusätzliche Herausforderung

Übung#python.m9.l4.e3
Versuche: 0Wird geladen…

Definiere einen Dekorator `double_result`, der den Rückgabewert der dekorierten Funktion verdoppelt (mit 2 multipliziert). Wende den Dekorator auf die Funktion `def sum_nums(a, b): return a + b` an. Evaluiere schließlich `sum_nums(3, 4)`.

Editor wird geladen…
Hinweis anzeigen

Der Wrapper ruft func(*args, **kwargs) auf und multipliziert das Ergebnis mit 2.

Lösung nach 3 Versuchen verfügbar