ProgrammierungBackend-Entwickler

Erklären Sie, wie Closures in Python funktionieren, wie sie sich von normalen Funktionen unterscheiden und was ihre praktische Anwendung ist?

Bestehen Sie Vorstellungsgespräche mit dem Hintsage-KI-Assistenten

Antwort.

Hintergrund der Frage

Der Begriff "Closure" wurde aus der funktionalen Programmierung entlehnt und ist seit Beginn in Python vorhanden. Closures ermöglichen es einer Funktion, die Umgebung, in der sie erstellt wurde, zu speichern, selbst wenn sie außerhalb dieser Umgebung aufgerufen wird. Dieses Konzept bietet Flexibilität und ermöglicht die Implementierung vieler Muster, einschließlich Funktionsfabriken und fauler Berechnungen.

Problem

In Python sind Funktionen Objekte erster Klasse. Manchmal ist es notwendig, dass eine eingebettete Funktion auf Variablen aus dem Geltungsbereich der umschließenden Funktion zugreift, selbst nachdem diese abgeschlossen ist. Die normale lexikalische Geltung garantiert dies beim Rückgabe der Funktion nicht. Wenn eine solche Funktion auf die Variablen ihrer Umgebung zugreift, entsteht ein Closure.

Lösung

Ein Closure entsteht, wenn die innere Funktion auf Variablen verweist, die in der äußeren definiert sind, und die äußere die innere nach außen zurückgibt. Dies wird häufig verwendet, um Funktionsfabriken zu erstellen, den Zustand ohne Klassen zu kapseln und eine Funktion mit "on-the-fly"-Parametern zu konstruieren.

Beispielcode:

def make_multiplier(factor): def multiplier(x): return x * factor return multiplier mul2 = make_multiplier(2) mul3 = make_multiplier(3) print(mul2(10)) # 20 print(mul3(10)) # 30

Wichtige Merkmale:

  • Ein Closure speichert die Werte der Umgebung, auch wenn die äußere Funktion bereits abgeschlossen ist.
  • Der Zustand in der inneren Funktion ist im Wesentlichen privat und kann außerhalb nicht direkt verändert werden.
  • Um eine nicht-immutable Variable in der äußeren Funktion innerhalb des Closures zu ändern, wird das Schlüsselwort nonlocal verwendet.

Fangfragen.

Kann ein Closure einen veränderbaren Zustand zwischen den Aufrufen beibehalten, wenn die Variable in der inneren Funktion geändert wird?

Ja, wenn in der inneren Funktion das Schlüsselwort nonlocal verwendet wird. Ohne nonlocal erstellt eine Zuweisung eine neue lokale Variable, ohne die äußere zu ändern.

def counter(): count = 0 def inc(): nonlocal count count += 1 return count return inc c = counter() print(c()) # 1 print(c()) # 2

Können private Variablen in Python mithilfe von Closures anstelle von Klassen implementiert werden?

Ja, Closures bieten eine einfache Implementierung "privater" Variablen, die von außen nicht zugänglich sind, es sei denn, Getter/Setter werden in der inneren Funktion bereitgestellt.

Gilt die Closure-Anwendung nur für Funktionen? Kann ein Closure auch mit Lambdas in Python organisiert werden?

Ja, ein Closure kann auch mit Lambda-Ausdrücken gebildet werden, da sie in Bezug auf die Bindung von lexikalischen Variablen analog zu def sind.

def make_power(n): return lambda x: x ** n square = make_power(2) cube = make_power(3) print(square(4)) # 16 print(cube(2)) # 8

Typische Fehler und Antipatterns

  • Zu erwarten, dass ein Closure automatisch eine äußere Variable ändert, wenn sie innerhalb geändert wird, ohne nonlocal.
  • Veränderbare Objekte in Closures zu erfassen und sie zu mutieren, ohne sich der Schwierigkeiten beim Debuggen bewusst zu sein.
  • Eine Schleife zu verwenden, um Funktionen in einer Closure ohne die richtige Bindung von Variablen zu erstellen (Falle: "Alle Funktionen sehen den letzten Wert der Variable").

Beispiel aus dem Leben

Negativer Fall

Eine Funktionsfabrik, die Handler in einer Schleife erstellt, verwendet die Schleifenvariable innerhalb des Closures:

handlers = [] for i in range(3): def handler(x): return x + i handlers.append(handler) print([h(10) for h in handlers]) # [12, 12, 12]

Vorteile:

  • Einfach, wenig Code.

Nachteile:

  • Alle Handler verweisen auf dieselbe Variable i, ihr letzter Wert ist 2 — das Verhalten ist für die meisten unerwartet.

Positiver Fall

Ein Standardargument wurde verwendet, um den Wert "festzulegen":

handlers = [] for i in range(3): def handler(x, j=i): return x + j handlers.append(handler) print([h(10) for h in handlers]) # [10, 11, 12]

Vorteile:

  • Bindung des erforderlichen Wertes.
  • Vorhersehbares Verhalten.

Nachteile:

  • Man muss sich an diese Feinheit erinnern und das Closure manuell korrigieren, was die Wartbarkeit des Codes erschwert.