Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.

Link zu dieser Vergleichsansicht

Nächste Überarbeitung
Vorhergehende Überarbeitung
modul:m323:learningunits:lu05:closures [2024/03/28 14:07] – angelegt - Externe Bearbeitung 127.0.0.1modul:m323:learningunits:lu05:closures [2026/09/01 11:35] (aktuell) – Korrektur: Anzahl Muster und Sortierbeispiel mit unterscheidbarer Ausgabe admin
Zeile 24: Zeile 24:
  
 In diesem Beispiel ist ''inner_function'' ein Closure, das den Wert von ''x'' speichert, selbst nachdem ''outer_function'' beendet wurde. In diesem Beispiel ist ''inner_function'' ein Closure, das den Wert von ''x'' speichert, selbst nachdem ''outer_function'' beendet wurde.
 +
 +===== Animation: was im Speicher passiert =====
 +
 +Die Animation zeigt genau dieses Beispiel Schritt für Schritt: wie der Rahmen von ''outer_function'' vom Aufruf-Stack verschwindet, während die Zelle mit ''x = 5'' bestehen bleibt. Der zweite Tab zeigt, dass zwei Aufrufe zwei **unabhängige** Zellen erzeugen.
 +
 +{{url>https://templates-python.github.io/m323_html/lu05/closures.html 100%,860px noborder|Animation: Closures, was die Funktion überlebt}}
  
 ===== Wie Closures arbeiten ===== ===== Wie Closures arbeiten =====
Zeile 31: Zeile 37:
   * Das ermöglicht es, Zustand und Daten zwischen mehreren Aufrufen der inneren Funktion zu speichern.   * Das ermöglicht es, Zustand und Daten zwischen mehreren Aufrufen der inneren Funktion zu speichern.
  
-===== Verwendungszwecke =====+===== Closures in der Praxis =====
  
-  * **Zustandsspeicherung**: Zum Speichern von Zuständen zwischen verschiedenen Aufrufen. +''add_five'' zeigt den Mechanismus, aber niemand schreibt so etwas in echtem Code – dafür gibt es die Addition. Die folgenden fünf Muster sind die Fälle, in denen Closures tatsächlich eingesetzt werden.
-  * **Datenkapselung**: Zum Verbergen von Daten und Zuständen, die nur in einem bestimmten Kontext relevant sind. +
-  * **Funktionsfabriken**: Zum Erzeugen von Funktionen mit bestimmten Eigenschaften oder Verhalten. +
-  * **Decorators**: Closures sind die Grundlage für die Implementierung von Decorators in Python.+
  
-<WRAP center round info 60%> +<WRAP center round info 90%> 
-Closures sind ein leistungsfähiges Konzept, das in vielen verschiedenen Kontexten in Python verwendet wird. Sie ermöglichen komplexere und modularere Programmstrukturen.+**Die Faustregel:** Ein Closure lohnt sich, wenn man eine Funktion braucht, die **eine bestimmte Einstellung schon kennt** – und man diese Einstellung nicht bei jedem Aufruf erneut mitgeben will. 
 +</WRAP> 
 + 
 +==== 1. Funktionsfabrik: konfigurierte Funktionen erzeugen ==== 
 + 
 +Statt bei jedem Aufruf den Steuersatz mitzugeben, erzeugt man einmal eine Funktion, die ihn bereits kennt: 
 + 
 +<code python> 
 +def preis_mit_steuer(satz): 
 +    def berechne(netto): 
 +        return round(netto * (1 + satz / 100), 2) 
 +    return berechne 
 + 
 +schweiz = preis_mit_steuer(8.1) 
 +deutschland = preis_mit_steuer(19) 
 + 
 +print(schweiz(100))       # 108.1 
 +print(deutschland(100))   # 119.0 
 +</code> 
 + 
 +''schweiz'' und ''deutschland'' sind zwei unabhängige Funktionen mit je eigener gespeicherter Einstellung – genau das zeigt Tab 2 der Animation. Typische Vertreter: Rabattrechner, Einheitenumrechner, Validatoren mit unterschiedlichen Grenzwerten. 
 + 
 +==== 2. Kriterien für ''sorted()'' und ''filter()'' erzeugen ==== 
 + 
 +Funktionen wie ''sorted()'' erwarten eine Funktion mit genau einem Parameter. Wenn diese Funktion aber zusätzlich wissen muss, **wonach** sortiert werden soll, liefert ein Closure die Lösung: 
 + 
 +<code python> 
 +def nach_feld(feld): 
 +    def schluessel(eintrag): 
 +        return eintrag[feld] 
 +    return schluessel 
 + 
 +produkte = [ 
 +    {"name": "Maus", "preis": 25}, 
 +    {"name": "Tastatur", "preis": 80}, 
 +    {"name": "USB-Hub", "preis": 12}, 
 +] 
 + 
 +for p in sorted(produkte, key=nach_feld("preis")): 
 +    print(p["name"], end=" ")   # USB-Hub Maus Tastatur 
 +print() 
 +for p in sorted(produkte, key=nach_feld("name")): 
 +    print(p["name"], end=" ")   # Maus Tastatur USB-Hub 
 +</code> 
 + 
 +So wird aus dem Klick auf eine Tabellenspalte im UI direkt die passende Sortierfunktion – ohne für jede Spalte eine eigene Funktion zu schreiben. 
 + 
 +==== 3. Zustand kapseln statt globaler Variablen ==== 
 + 
 +Ein Closure kann sich zwischen den Aufrufen etwas merken. Mit ''nonlocal'' darf die innere Funktion die gespeicherte Variable auch verändern: 
 + 
 +<code python> 
 +def zaehler_erstellen(): 
 +    anzahl = 0 
 +    def hochzaehlen(): 
 +        nonlocal anzahl 
 +        anzahl += 1 
 +        return anzahl 
 +    return hochzaehlen 
 + 
 +klicks = zaehler_erstellen() 
 +fehler = zaehler_erstellen() 
 + 
 +print(klicks())   # 1 
 +print(klicks())   # 2 
 +print(fehler())   # 1  -> eigener, unabhängiger Zustand 
 +</code> 
 + 
 +Der entscheidende Punkt: ''anzahl'' ist von außen **nicht erreichbar**. Niemand kann den Zähler versehentlich auf 500 setzen. Das ist echte Datenkapselung – ohne globale Variable und ohne eigene Klasse. Genau darum geht es in der Aufgabe [[modul:m323:learningunits:lu05:aufgaben:closures2|LU05.A09 - Refactoring: Vermeiden globaler Variablen durch Closures]]. 
 + 
 +==== 4. Callbacks in Oberflächen und Event-Systemen ==== 
 + 
 +Ein Button erwartet eine Funktion ohne Argumente. Trotzdem muss beim Klick klar sein, //welcher// Button gedrückt wurde – das Closure transportiert diese Information: 
 + 
 +<code python> 
 +def klick_handler(button_name): 
 +    def bei_klick(): 
 +        print(f"Button '{button_name}' wurde geklickt") 
 +    return bei_klick 
 + 
 +# button.on_click(klick_handler("Speichern")) 
 +klick_handler("Speichern")()   # Button 'Speichern' wurde geklickt 
 +</code> 
 + 
 +Dasselbe Muster steckt hinter Event-Listenern in JavaScript, Callbacks in GUI-Frameworks und Retry-Logik, die eine bereits konfigurierte Operation erneut ausführt. 
 + 
 +==== 5. Decorators ==== 
 + 
 +Decorators sind die verbreitetste Anwendung von Closures überhaupt: Die äußere Funktion nimmt eine Funktion entgegen, die innere „erinnert" sich an sie und erweitert ihr Verhalten – etwa um Logging, Zeitmessung oder eine Berechtigungsprüfung. Details dazu auf [[modul:m323:learningunits:lu05:decorator|LU05d - Decorators für Funktionen in Python]]. 
 + 
 +<WRAP center round important 90%> 
 +**Stolperfalle: späte Auswertung in Schleifen** 
 + 
 +Ein Closure speichert die **Variable**, nicht deren Wert zum Zeitpunkt der Definition. In einer Schleife führt das zu einem klassischen Fehler: 
 + 
 +<code python> 
 +funktionen = [lambda: i for i in range(3)] 
 +print([f() for f in funktionen])   # [2, 2, 2] – nicht [0, 1, 2]! 
 +</code> 
 + 
 +Alle drei Funktionen teilen sich dieselbe Variable ''i'', die am Ende 2 ist. Die Lösung ist ein Default-Argument, das den Wert sofort kopiert: 
 + 
 +<code python> 
 +funktionen = [lambda i=i: i for i in range(3)] 
 +print([f() for f in funktionen])   # [0, 1, 2] 
 +</code>
 </WRAP> </WRAP>
  
 ---- ----
-{{tag>M323-LU05}}+{{tag>M323-LU05 M323-C2I}}
 [[https://creativecommons.org/licenses/by-nc-sa/4.0/ch/|{{https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png}}]] (c) Kevin Maurizi  [[https://creativecommons.org/licenses/by-nc-sa/4.0/ch/|{{https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png}}]] (c) Kevin Maurizi 
 +
  • modul/m323/learningunits/lu05/closures.1711631267.txt.gz
  • Zuletzt geändert: 2024/03/28 14:07
  • von 127.0.0.1