R

ReflexKR

Reflex-Dokumentation

Der Etiketten-Flughafen: Pythons Objektmodell

·

Kursüberblick

1

Das Etikett ist nicht der Koffer: Namen und Objekte

2

Versiegelte Koffer: veränderlich und unveränderlich

3

Koffer im Koffer: Container und Kopien

4

Wenn ein Koffer übergeben wird: Funktionen und Argumente

5

Zonen und Ausweise: Gültigkeitsbereiche

6

Handgepäck: Closures

7

Der Koffer-Bauplan: Klassen und Instanzen

8

Flughafen-Standardverfahren: Dunder-Methoden

9

Der Bauplan hinter dem Bauplan: Vererbung und MRO

10

Die Kontrolle: das Geheimnis des Attributzugriffs

11

Herrenlose Koffer: Speicher und GC

12

Schließfachnummern: Hash, Gleichheit und Gesamtbild

Der Etiketten-Flughafen: Pythons Objektmodell
Lektion 5

Zeit 3:20–4:10

Zonen und Ausweise: Gültigkeitsbereiche

Verlustmeldung

LOST-005

Der Name steht direkt daneben und wird nicht gefunden

Gemeldet von

Han, Betriebsteam der Sicherheitskontrolle

Was fehlt

Ein Zähler für die durchleuchteten Koffer. Ganz oben steht eindeutig scanned = 0, doch sobald scan() aufgerufen wird, kommt ein UnboundLocalError.

Erledigt, wenn

Erklären, warum die Funktion den Namen draußen nicht sieht, und den Zähler so reparieren, dass er richtig zählt.

Internationaler Flughafen Haneulseom · Gepäckzentrum

Schichtleiterin Do Miran tippt auf den Flughafenplan an der Wand. „Gate, Terminal, Flughafen, Land. Ausweise werden von innen nach außen geprüft. Wer innen gefunden wird, für den schaut man außen gar nicht mehr nach.“

Die heutige Meldung betrifft den Zähler an der Sicherheitskontrolle. Draußen steht scanned = 0, und die Funktion sollte den Wert jeweils um eins erhöhen. Doch schon in der ersten Zeile kommt ein Fehler, so einen Wert gebe es nicht. Die Schichtleiterin zeigt auf die unterste Zeile der Funktion. „Hier ist der Täter. Die Zeile, die einen Wert zuweist.“

5-1. Die Reihenfolge der Namenssuche: LEGB (9 min)

Wenn Python einen Namen liest, sucht es in vier Bereichen der Reihe nach von innen nach außen. Beim ersten Treffer hört es auf.

ReihenfolgeBereichWasAm Flughafen wäre das
LLokal (Local)in der gerade laufenden Funktiondas Gate
EUmschließend (Enclosing)die äußeren Funktionen, die diese Funktion umschließendas Terminal
GGlobaldas Modul, in dem die Funktion definiert istder Flughafen
BEingebaut (Built-in)Grundnamen wie len, print, listdas Land
zone = "G"

def terminal():
    gate = "E"
    def boarding():
        seat = "L"
        print(seat, gate, zone, len("abc"))
    boarding()

terminal()
# L E G 3
  • seat wird in boarding selbst (L) gefunden, gate in der umschließenden Funktion terminal (E).
  • zone wird im Modul (G) gefunden, len bei den eingebauten Namen (B).
  • Steht ein Name in keinem der vier Bereiche, gibt es einen NameError.
  • Die Bereiche hängen nicht davon ab, wo eine Funktion aufgerufen wird, sondern davon, wo sie geschrieben steht. Auch wenn boarding von woanders aufgerufen würde, käme gate aus terminal.

Die Gültigkeitskarte unten führt den Code nicht aus, sondern liest ihn nur und zeigt für jeden Namen, in welchem Bereich er gefunden wird. Die letzte Funktion broken ist die Hauptfigur des nächsten Abschnitts.

Gültigkeitskarte

1

zone = "G"

2

3

def terminal():

4

gate = "E"

5

def boarding():

6

seat = "L"

7

print(seat, gate, zone, len("abc"))

8

boarding()

9

10

def broken():

11

print(zone)

12

zone = "L"

Name

Die vier Namen in boarding werden in L, E, G und B gefunden. zone in broken bleibt in L hängen, obwohl es den Namen draußen gibt.
  • Wähl einmal seat @ terminal. seat ist ein lokaler Name der inneren Funktion boarding und ist deshalb in der äußeren Funktion terminal nicht sichtbar. Gesucht wird immer von innen nach außen.
  • zone @ broken wird in L mit „Hier gefunden“ markiert. Das heißt nur, dass der Name als lokaler Name dieser Funktion festgelegt ist, nicht, dass er schon einen Wert hat.

5-2. Wo zugewiesen wird, bestimmt den Gültigkeitsbereich (10 min)

Das ist der Code aus der Meldung:

scanned = 0

def scan(bag):
    print(bag, scanned)
    scanned = scanned + 1

scan("T1")
# UnboundLocalError: cannot access local variable 'scanned' where it is not associated with a value

Der Fehler kommt nicht in Zeile 5, sondern schon beim print in Zeile 4. Zeile 5 ist noch gar nicht gelaufen. Trotzdem sucht Python das scanned in Zeile 4 nicht draußen.

Es gibt genau eine Regel: Wird einem Namen irgendwo in einer Funktion ein Wert zugewiesen, ist dieser Name in der gesamten Funktion lokal. Diese Entscheidung fällt nicht bei der Ausführung, sondern beim Kompilieren der Funktion.

def scan(bag):
    print(bag, scanned)
    scanned = scanned + 1

print(scan.__code__.co_varnames)
# ('bag', 'scanned')
print(issubclass(UnboundLocalError, NameError))
# True
  • co_varnames ist die Liste der lokalen Namen, die der Compiler festgelegt hat. scanned steht schon darin.
  • Zeile 4 sucht also das „lokale Etikett scanned“, und dieses Etikett hängt noch an keinem Koffer. Genau das sagt die Fehlermeldung.
  • UnboundLocalError ist eine Unterart von NameError.

Auch Zeilen, die nie ausgeführt werden, zählen mit:

x = 10

def f():
    print(x)
    if False:
        x = 1

f()
# UnboundLocalError: cannot access local variable 'x' where it is not associated with a value

„Einen Wert zuweisen“ heißt nicht nur =. Auch +=, for x in ..., import x, def x():, class x:, with ... as x, except ... as x und del x machen einen Namen in dieser Funktion lokal.

Vergleich auf der Etikettentafel unten die drei Varianten. Die erste ist der Code aus der Meldung, im letzten Schritt wird der Fehler angezeigt.

Etikettentafel

Schritt 1 / 0

Als Nächstes läuft: 0

Labor wird vorbereitet…

Gerade geändert

Versiegelter Koffer (unveränderlich)

In der ersten Variante hat der Rahmen scan nur das Etikett bag, das Etikett scanned ist leer. global benutzt das Etikett draußen, und die dritte Variante nimmt den Wert als Argument entgegen und gibt ihn zurück.

Pin sagt

→ Siehst du einen UnboundLocalError, schau unterhalb der fehlerhaften Zeile nach. Irgendwo in derselben Funktion wird diesem Namen ein Wert zugewiesen.

5-3. Etiketten draußen direkt umhängen: global und nonlocal (7 min)

Willst du einem Namen in einem äußeren Bereich einen Wert zuweisen, musst du das vorher erklären.

scanned = 0

def scan(bag):
    global scanned
    scanned = scanned + 1

scan("T1")
scan("T2")
print(scanned)
# 2
def terminal():
    boarded = 0
    def board():
        nonlocal boarded
        boarded += 1
    board()
    board()
    return boarded

print(terminal())
# 2
  • global x: In dieser Funktion ist x der Name aus dem Modul. Eine Zuweisung hängt das Etikett im Modul um.
  • nonlocal x: In dieser Funktion ist x der Name aus der nächsten umschließenden Funktion. Für Modulnamen geht das nicht.
  • Gibt es den Namen für nonlocal in keiner umschließenden Funktion, kommt schon vor der Ausführung SyntaxError: no binding for nonlocal 'x' found.
  • Die Erklärung muss vor der ersten Verwendung stehen. Wer zuerst zuweist und erst danach global x schreibt, bekommt SyntaxError: name 'x' is assigned to before global declaration.

Zum bloßen Lesen braucht man keine Erklärung. boarding in 5-1 hat gate und zone ohne jede Erklärung gelesen. Nötig ist sie nur beim Zuweisen.

global ändert heimlich Zustand außerhalb der Funktion und macht es dadurch schwer, die Funktion für sich zu testen. Meist ist es besser, den Wert als Argument zu übergeben und den neuen Wert zurückzugeben. nonlocal kommt in Lektion 6 bei den Closures wieder vor.

5-4. Was einen Bereich erzeugt und was nicht (5 min)

In Python erzeugen Funktionen (def, lambda), Comprehensions und Klassenrümpfe einen neuen Bereich. Blöcke mit if, for, while, with und try tun das nicht.

for i in range(3):
    pass
print(i)
# 2

if True:
    inside = "if"
print(inside)
# if

sq = [n * n for n in range(3)]
print(sq)
# [0, 1, 4]
print(n)
# NameError: name 'n' is not defined
Erzeugt einen BereichErzeugt keinen Bereich
def, lambda, List-, Set- und Dict-Comprehensions, Generatorausdrücke, Rumpf von classif, for, while, with, try
  • Auch nach dem Ende von for bleibt die Schleifenvariable i mit ihrem letzten Wert (2) bestehen. Für Namen, die in der Schleife entstehen, gilt dasselbe.
  • Das n der Comprehension sickert nicht nach draußen. Seit 3.12 baut CPython Comprehensions nicht mehr als eigene Funktion, sondern führt sie eingebettet aus (PEP 709). Die Regel, dass ihre Namen nicht nach außen dringen, gilt aber weiterhin.

5-5. Eingebaute Namen verdecken (5 min)

Die eingebauten Namen liegen ganz außen (B). Legst du global einen gleichnamigen Namen an, wird von da an zuerst der globale gefunden.

list = ["T1", "T2"]
print(list("ab"))
# TypeError: 'list' object is not callable

Das Etikett list hängt global an einem Listenkoffer, also versucht list("ab"), diese Liste aufzurufen, und scheitert. Das eingebaute list ist nicht verschwunden, es ist nur verdeckt.

list = ["T1", "T2"]
del list
print(list("ab"))
# ['a', 'b']

import builtins
print(builtins.list is list, builtins.len("abc"))
# True 3
  • Nimmst du das globale Etikett list ab, ist das eingebaute list wieder sichtbar.
  • Die eingebauten Namen stehen im Modul builtins. Auch wenn sie verdeckt sind, kommst du über builtins.list jederzeit an sie heran.
  • Hier ist die Antwort auf Übung 3 aus Lektion 1: Führst du nach shout = print den Befehl del print aus, kommt NameError: name 'print' is not defined. Global gibt es kein Etikett print, also gibt es auch nichts abzunehmen.

Verwende Namen wie list, dict, str, id, type, input, max oder sum nicht als Variablen. Sonst verdeckst du in dieser Datei die eingebauten Funktionen.

5-6. In Bereiche hineinsehen: locals(), globals(), vars(), dir() (4 min)

  • globals(): das Dictionary des Modulbereichs. Im Modul aufgerufen, ist es der echte Speicher selbst.
  • locals(): die Namen im aktuellen Bereich. Im Modul ist es dasselbe wie globals().
  • vars(obj): das __dict__ eines Objekts. Ohne Argument entspricht es locals() (Lektion 7).
  • dir(): gibt nur die Liste der Namen sortiert zurück.

Seit 3.13 ist die Bedeutung von locals() in Funktionen eindeutig festgelegt (PEP 667). Jeder Aufruf liefert eine Momentaufnahme als neues Dictionary. Änderst du dieses Dictionary, ändern sich die lokalen Variablen nicht.

def check():
    y = 1
    locals()["y"] = 99
    print(y, locals()["y"])
    snap = locals()
    y = 2
    print(snap["y"], locals()["y"])

check()
# 1 1
# 1 2

def where(a):
    b = 2
    return dir(), vars()

print(where(1))
# (['a', 'b'], {'a': 1, 'b': 2})
  • locals()["y"] = 99 ändert nur die gerade erzeugte Momentaufnahme. Die Variable y bleibt 1.
  • snap behält den Wert vom Moment der Aufnahme (1). Erst ein erneuter Aufruf von locals() zeigt die 2.
  • Wenn du Namen dynamisch erzeugen willst, nimm ein Dictionary statt locals().

Fall abgeschlossen (10 min)

Für den Zähler gibt es zwei Lösungen: das Etikett draußen direkt umhängen (global) oder den Wert entgegennehmen und den neuen Wert zurückgeben.

scanned = 0

def scan(bag):
    global scanned
    print(bag, scanned)
    scanned = scanned + 1

scan("T1")
scan("T2")
print(scanned)
# T1 0
# T2 1
# 2

def scan_count(bag, count):
    print(bag, count)
    return count + 1

total = 0
total = scan_count("T1", total)
total = scan_count("T2", total)
print(total)
# T1 0
# T2 1
# 2

Han entscheidet sich für die zweite Variante. Selbst wenn es später mehrere Kontrollstellen gibt, kann so jede für sich zählen. Die Schichtleiterin fügt hinzu: „Jede Zone hat ihre eigenen Etiketten. Wer ein Etikett in einer fremden Zone umhängen will, zeigt zuerst seinen Ausweis.“

Abschlussbericht — LOST-005. Durch scanned = ... weiter unten war scanned in der ganzen Funktion lokal, daher der UnboundLocalError gleich in der ersten Zeile. Gelöst, indem der Wert als Argument übergeben und zurückgegeben wird.

Übungen

  1. Was passiert, wenn du in boarding aus 5-1 unterhalb des print die Zeile zone = "L" einfügst? Vergleich das mit zone @ broken auf der Gültigkeitskarte.
  2. Bau ohne nonlocal einen Zähler, der per append an eine Liste der äußeren Funktion zählt. Warum funktioniert das ohne Erklärung? (Tipp: Etikett umhängen oder Koffer verändern.)
  3. Welcher Fehler kommt, wenn ganz oben im Modul sum = 0 steht und du dann sum([1, 2, 3]) aufrufst? Behebe ihn mit builtins.

Das Notizbuch der Schichtleiterin

  • Namen werden in der Reihenfolge L → E → G → B gesucht, Schluss ist beim ersten Treffer.

  • Der Bereich hängt nicht davon ab, wo aufgerufen wird, sondern davon, wo geschrieben wurde.

  • Wird irgendwo in einer Funktion zugewiesen, ist der Name in der ganzen Funktion lokal. Daher der UnboundLocalError.

  • global und nonlocal nur zum Umhängen äußerer Etiketten. Zum Lesen braucht man sie nicht.

  • Schleifen und if erzeugen keinen Bereich, Funktionen und Comprehensions schon.

  • list = … verdeckt das eingebaute list. Eingebaute Namen stehen in builtins.

  • locals() in einer Funktion ist eine Momentaufnahme (3.13, PEP 667). Änderungen daran ändern die Variablen nicht.

← Zurück

Lektion 4 · Wenn ein Koffer übergeben wird: Funktionen und Argumente

Weiter →

Lektion 6 · Handgepäck: Closures

Über neue Lektionen informiert werden

Wir schicken dir eine E-Mail, wenn dieser Kurs neue Lektionen bekommt oder ein Folgekurs erscheint.

Erhoben: deine E-Mail-Adresse · Zweck: Hinweise auf neue Lektionen oder den Start dieses Kurses · Speicherdauer: bis du die Löschung verlangst. Keine andere Nutzung, keine Weitergabe. Antworte einfach auf eine Benachrichtigung, dann löschen wir sie sofort.

Fragen & Antworten

Frag alles zu dieser Lektion. Fragen aus allen Sprachversionen dieser Seite erscheinen hier gemeinsam.

Wird als reiner Text angezeigt, ohne Links oder Formatierung.

R

ReflexKR

Python und Reflex zum Anfassen. Alle Beispiele wurden mit Reflex 0.10.0 ausgeführt und geprüft.

ReflexKR ist kostenlos und ohne Werbebanner. Wenn es dir geholfen hat, kannst du es mit einem Kaffee unterstützen. ☕ Spendier uns einen Kaffee

Hinweis zu Affiliate-Links
한국어English日本語简体中文EspañolDeutsch

© 2026 ReflexKR · Since 2026 · CC BY-NC 4.0

Built with Reflex