Python Scopes (Sichtbarkeitsbereich) (C) 2017-2024 T.Birnthaler OSTC GmbH ==================================== --> python-scope.txt Scope/Sichtbarkeitsbereich --> python-namespace.txt Namespace/Namensraum --> python-lifetime.txt Lebensdauer/Lifetime/Existenz/Gültigkeitsbereich Um auf den hinter einem NAMEN steckenden WERT/OBJEKT zuzugreifen, werden gemäß folgender "LEGB-Regel" alle Scopes (Sichtbarkeitsbereiche) in einem Programm in folgender REIHENFOLGE von oben nach unten (d.h. von "innen nach außen") nach diesem Namen durchsucht ("NAME-LOOKUP"). Ein SCOPE ist eine NAMENS-TABELLE, zu jedem Namen ist darin eine REFERENZ auf den aktuell ihm zugeordneten WERT/OBJEKT gespeichert. Beim ersten Treffer ist die Suche zu Ende, ist der Name in keinem Scope vorhanden, bricht Python das Programm mit einem "NameError"-Fehler ab. +------------+---+------------------------------------------------+ || | L)ocal | 1 | Funktion (auch lambda, Comprehension) | || innen | E)nclosing | 2 | Einschachtelnde Funktion (z.B. Dekorator) | || : | G)lobal | 3 | Hauptprogramm (Skript, Modul) | || : | B)uilt-in | 4 | Python-Interpreter (z.B. len, sum, print, ...) | || außen +------------+---+------------------------------------------------+ \/ ACHTUNG: In Python gibt es KEINEN BLOCK-SCOPE, d.h. keine block-lokalen Namen. BLÖCKE von zusammengehörenden Anweisungen (= GROSSE Anweisung) werden in Python durch gleichförmiges Einrücken nach einem ":" gebildet (in vielen anderen Sprachen durch Einschachteln in geschweifte Klammern {...}). ACHTUNG: Auch LAUFVARIABLE von for-Schleifen sind nicht lokal zur Schleife (und enthalten daher nach der Schleife den letzten in der Schleife erreichten Wert): var = 0 # var ist globale Variable (i, j unbekannt) for i in range(1,10+1): # i ist globale Variable (j unbekannt j = 10 # j ist globale Variable var += i # var, i sind globale Variable print(var, i, j) # var, i, j sind globale Variable Folgendes Code-Stück zeigt das LEGB-Verhalten beim "Name-Lookup" (Spalte "R" = Ausgabe-Reihenfolge) +------------------------+--------------------+---+-------------------------+ | Code | Aktion | R | Ausgabe | +------------------------+--------------------+---+-------------------------+ | print(len) | | 1 | <built-in function len> | | def f_aussen(): | Def. Funktion | | | | len = "E)nclosing" | Def. lok. Var. | | | | print(len) | | 3 | E)nclosing | | def f_innen(): | Def. Funktion | | | | len = "L)okal" | Def. lok. Var. | | | | print(len) | | 5 | L)ocal | | print(len) | | 4 | E)nclosing | | f_innen() | Aufruf "f_innen" | | | | print(len) | | 6 | E)nclosing | | print(len) | | 2 | <built-in function len> | | f_aussen() | Aufruf "f_aussen" | | | | print(len) | | 7 | <built-in function len> | | len = "G)lobal" | Def. glob. Var. | | | | print(len) | | 8 | G)lobal | | del len | Löschen glob. Var. | | | | print(len) | | 9 | <built-in function len> | +------------------------+--------------------+---+-------------------------+ Auf eine GLOBALE Variable kann in Funktionen LESEND zugegriffen werden, falls keine gleichnamige LOKALE Variable durch Zuweisung gebildet wird: gv = "global" # Globale Variable gv erzeugen # def f(...): # Funktion f() definieren print(gv) # Globale Variable gv lesen (OK) # def g(...): # Funktion g() definieren gv = 123 # Lokale Variable gv erzeugen (OK, gleichnamig) print(gv) # Lokale Variable gv lesen (OK) Erst eine globale Variable LESEN und dann eine gleichnamige lokale Variable ERZEUGEN ist nicht erlaubt (löst den Fehler "UnboundLocalError" mit der Meldung "local variable 'gv' referenced before assignment" aus): def g(...): # Funktion g() definieren print(gv) # Globale Variable gv lesen (OK) gv = 123 # Lokale Variable gv erzeugen (PENG, gleichnamig) Per Schlüsselwort "global" kann eine Funktion auf einen GLOBALEN Namen des Hauptprogramms LESEND + SCHREIBEND zugreifen (diese "Schweinerei" wird also durch das Schlüsselwort "global" deutlich sichtbar gemacht): gv = "global" # Globale Variable gv erzeugen # def f(...): # Funktion f() definieren global gv # Globale Variable gv lesen + schreiben erlauben print(gv) # Globale Variable gv lesen (OK) gv = "lokal" # Globale Variable gv schreiben (OK) print(gv) # Globale Variable gv lesen (OK) Per Schlüsselwort "nonlocal" kann eine Funktion auf einen LOKALEN Namen der sie direkt umgebenden (enclosing) "Wrapper"-Funktion zugreifen (LESEN + SCHREIBEN). def f(...): # Funktion f() definieren lv = "enclosing" # Lokale Variable lv in f() erzeugen # def g(...): # Eingeschachtelte (lokale) Funktion g() definieren nonlocal lv # Nichtlokale Variable lv von f() verwenden print(lv) # Nichtlokale Variable lv von f() lesen (OK) lv = "local" # Nichtlokale Variable lv von f() schreiben (OK) print(lv) # Nichtlokale Variable lv von f() lesen (OK) Auch mehrfach bei mehreren ineinander verschachtelten Funktionen möglich: def f(...): # Funktion f() definieren lv = "enclosing2" # Lokale Variable lv in f() erzeugen # def g(...): # Eingeschachtelte (lokale) Funktion g() definieren nonlocal lv # Nichtlokale Variable lv von f() verwenden print(lv) # Nichtlokale Variable lv von f() lesen (OK) lv = "enclosing1" # Nichtlokale Variable lv von f() schreiben (OK) print(lv) # Nichtlokale Variable lv von f() lesen (OK) # def h(...): # Eingeschachtelte (lokale) Funktion h() definieren nonlocal lv # Nichtlokale Variable lv von f() verwenden print(lv) # Nichtlokale Variable lv von f() lesen (OK) lv = "local" # Nichtlokale Variable lv von f() schreiben (OK) print(lv) # Nichtlokale Variable lv von f() lesen (OK) GEBUNDENE NAMEN in COMPREHENSIONS (die also von der Comprehension mit einem Wert gefüllt werden), haben LOCAL SCOPE, d.h. außerhalb der Comprehension sind sie nicht sichtbar: qz = [(a, b, c) for a in range(1,11) # a, b, c lokal zur Comprehension for b in range(1,11) for c in range(1,11) if a < b < c if a**2 + b**2 == c**2] text = "hallo welt wie geht es dir" freq = { z : text.count(z) for z in set(text) } # z lokal zur Comprehension Ebenso haben GEBUNDENE NAMEN in LAMBDA-FUNKTIONEN (die also vom Aufruf der lambda-Funktion mit einem Wert gefüllt werden) LOCAL SCOPE, d.h. außerhalb der lambda-Funktion sind sie ebenfalls nicht sichtbar: erg = sorted(..., key=lambda x: abs(x)) # x lokal zu lambda-Funktion