W3docs

Python Closures

Lerne, wie Python Closures funktionieren: verschachtelte Funktionen, erfasste Variablen, nonlocal, häufige Fallstricke und praktische Anwendungsfälle mit ausführbaren Beispielen.

Eine Closure ist eine verschachtelte Funktion, die sich die Variablen des umschließenden Scopes merkt, in dem sie definiert wurde – selbst nachdem die äußere Funktion bereits zurückgekehrt ist. Closures ermöglichen es, privaten Zustand an eine Funktion zu knüpfen, ohne eine Klasse zu verwenden – und machen sie zu einem der elegantesten Werkzeuge in Python für den Aufbau von Callbacks, Factories und zustandsbehafteten Hilfsfunktionen.

Diese Seite erklärt, wie Closures funktionieren, welche drei Bedingungen sie erfordern, häufige Fallstricke und praktische Anwendungsfälle.

Was ist eine Closure?

Wenn Python eine Funktion ausführt, erzeugt sie einen lokalen Scope, der verschwindet, sobald die Funktion zurückkehrt. Normalerweise ist jede dort definierte Variable dann weg. Eine Closure bildet die Ausnahme: Wenn eine innere Funktion auf eine Variable einer äußeren Funktion verweist, hält Python diese Variable in einem speziellen Cell-Objekt am Leben, und die innere Funktion trägt einen Verweis auf diese Cells mit sich – wohin auch immer sie weitergegeben wird.

Die einfachste Closure ist eine Funktions-Factory – eine Funktion, die eine andere Funktion erstellt und zurückgibt:

def make_multiplier(factor):
    def multiply(n):
        return n * factor   # 'factor' is captured from the enclosing scope
    return multiply

double = make_multiplier(2)
triple = make_multiplier(3)

print(double(5))   # 10
print(triple(5))   # 15
print(double(10))  # 20

Jeder Aufruf von make_multiplier erzeugt eine neue Closure mit einer eigenen unabhängigen Kopie von factor. double und triple sind völlig unabhängig voneinander, obwohl sie durch dieselbe Funktion erstellt wurden.

Drei Bedingungen für eine Closure

Eine Funktion ist eine Closure, wenn alle drei der folgenden Bedingungen zutreffen:

  1. Es gibt eine verschachtelte Funktion – eine Funktion, die innerhalb einer anderen Funktion definiert ist.
  2. Die verschachtelte Funktion referenziert eine Variable aus dem umschließenden Scope – diese Variable wird als freie Variable bezeichnet.
  3. Die umschließende Funktion gibt die verschachtelte Funktion zurück (oder übergibt sie an eine andere Stelle).
def outer():
    message = 'Hello from outer'  # free variable
    def inner():
        print(message)            # inner references it
    return inner                  # outer returns inner

greet = outer()
greet()   # Hello from outer

Nachdem outer() zurückgekehrt ist, ist ihr lokaler Frame verschwunden – aber message überlebt innerhalb von greet.__closure__.

Eine Closure inspizieren

Python macht Closure-Cells über das Attribut __closure__ zugänglich:

def make_adder(n):
    def add(x):
        return x + n
    return add

add5 = make_adder(5)
print(add5(3))                            # 8
print(add5.__closure__)                   # (<cell at 0x...>,)
print(add5.__closure__[0].cell_contents)  # 5

__closure__ ist ein Tupel aus Cell-Objekten – eines pro erfasster Variable. Wenn eine Funktion keine Closure ist, ist __closure__ gleich None.

Erfasste Variablen mit nonlocal ändern

Standardmäßig kann man eine erfasste Variable lesen, aber nicht neu binden. Der Versuch, ihr einen Wert zuzuweisen, erstellt stattdessen eine neue lokale Variable, was in der Regel nicht das Gewünschte ist. Verwende das Schlüsselwort nonlocal, um Python mitzuteilen, dass die Variable des umschließenden Scopes gemeint ist:

def make_counter(start=0):
    count = start
    def increment(step=1):
        nonlocal count      # rebind the enclosing 'count', not a new local
        count += step
        return count
    return increment

counter = make_counter()
print(counter())    # 1
print(counter())    # 2
print(counter(5))   # 7

counter2 = make_counter(10)
print(counter2())   # 11
print(counter())    # 8  — counter is unaffected

Jeder Aufruf von make_counter erzeugt eine unabhängige count-Cell. counter und counter2 teilen keinen gemeinsamen Zustand.

Für einen tieferen Einblick in die Frage, wie Python entscheidet, zu welchem Scope eine Variable gehört, siehe Python Scope.

Häufiger Fallstrick: Closures in Schleifen

Ein klassischer Fehler ist das Erstellen von Closures innerhalb einer Schleife in der Erwartung, dass jede den aktuellen Wert der Schleifenvariable erfasst:

# Wrong — all functions capture the same 'i' cell
funcs = []
for i in range(3):
    funcs.append(lambda: i)

print([f() for f in funcs])  # [2, 2, 2]  — not [0, 1, 2]

Alle drei Lambdas teilen sich eine Cell, die die Schleifenvariable i enthält. Wenn sie aufgerufen werden, hat i bereits seinen Endwert 2 erreicht.

Lösung 1: Standardargument (erfasst nach Wert)

funcs = []
for i in range(3):
    funcs.append(lambda i=i: i)   # default arg is evaluated immediately

print([f() for f in funcs])  # [0, 1, 2]

Lösung 2: Factory-Funktion

def make_func(i):
    def f():
        return i
    return f

funcs = [make_func(i) for i in range(3)]
print([f() for f in funcs])  # [0, 1, 2]

Die Factory-Funktion erzeugt für jede Iteration einen neuen Scope – und damit eine neue Cell. Dies ist der explizitere und besser lesbare Ansatz.

Praktische Anwendungsfälle

Partielle Anwendung

Closures sind eine leichtgewichtige Alternative zu functools.partial, wenn man eine vorkonfigurierte Version einer Funktion benötigt:

def make_power(exponent):
    def power(base):
        return base ** exponent
    return power

square = make_power(2)
cube   = make_power(3)

print(square(4))  # 16
print(cube(3))    # 27

Einfaches Memoization

Eine Closure kann ein Cache-Dictionary halten, das zwischen Aufrufen erhalten bleibt:

def make_memoized(func):
    cache = {}
    def wrapper(*args):
        if args not in cache:
            cache[args] = func(*args)
        return cache[args]
    return wrapper

@make_memoized
def slow_square(n):
    return n * n

print(slow_square(4))   # 16
print(slow_square(4))   # 16 (served from cache)
print(slow_square(7))   # 49

Dieses Muster zeigt genau, wie Python Decorators im Hintergrund funktionieren – ein Decorator ist einfach eine Closure, die eine andere Funktion umhüllt.

Callback-Konfiguration

Closures sind praktisch für den Aufbau von Callbacks, die ein wenig Kontext eingebettet benötigen:

def make_logger(prefix):
    def log(message):
        print(f'[{prefix}] {message}')
    return log

info  = make_logger('INFO')
error = make_logger('ERROR')

info('Server started')    # [INFO] Server started
error('Disk full')        # [ERROR] Disk full

Closures vs. Klassen

Eine Closure und eine Klasse mit einer einzelnen Methode lösen oft dasselbe Problem. Die Wahl hängt von der Komplexität ab:

SituationBevorzuge
Ein Zustandselement, ein VerhaltenClosure
Mehrere Methoden oder öffentliche AttributeKlasse
Muss serialisiert werden (z. B. pickle)Klasse
Übergabe eines Callbacks an eine andere FunktionClosure
# Class approach
class Counter:
    def __init__(self, start=0):
        self.count = start
    def increment(self, step=1):
        self.count += step
        return self.count

# Closure approach
def make_counter(start=0):
    count = start
    def increment(step=1):
        nonlocal count
        count += step
        return count
    return increment

Beide erzeugen identisches Verhalten. Die Closure ist kürzer; die Klasse ist leichter auffindbar und erweiterbar.

Fazit

Closures ermöglichen es einer verschachtelten Funktion, ihren eigenen privaten Zustand zu tragen, indem sie sich die Variablen aus dem Scope merkt, in dem sie definiert wurde. Die wichtigsten Punkte sind:

  • Eine Closure erfordert eine verschachtelte Funktion, eine freie Variable und die Rückgabe oder Weitergabe der inneren Funktion.
  • Verwende nonlocal, wenn du eine erfasste Variable neu binden (nicht nur lesen) möchtest.
  • Vermeide den Schleifen-Fallstrick: Verwende eine Factory-Funktion oder ein Standardargument, um den Wert bei jeder Iteration festzuhalten.
  • Closures sind die Grundlage von Decorators und stehen in engem Zusammenhang mit Pythons Scope-Regeln.

Übungen

Übung
Which keyword allows an inner function to rebind a variable from its enclosing scope?
Which keyword allows an inner function to rebind a variable from its enclosing scope?
Übung
What does the __closure__ attribute return when a function is NOT a closure?
What does the __closure__ attribute return when a function is NOT a closure?
Was this page helpful?