Zum Inhalt springen
aviral gupta

// I2.1 · ca. 30 Min. · Aufbau

Gültigkeitsbereiche und Namensräume

Nach dieser Lektion sagen Sie vorher, welche Variable ein Name meint, ändern äußere Variablen mit global und nonlocal und beheben einen UnboundLocalError.

Lektion 1 von 6 in I2 Klassen

Anfang des Moduls

Danach können Sie

  • Vorhersagen, welche Bindung ein Name meint, entlang der lokalen, umschließenden, globalen und eingebauten Scopes
  • Einen Modulnamen mit global und einen umschließenden Namen mit nonlocal neu binden und erkennen, wann keins von beiden nötig ist
  • Erklären, warum eine Zuweisung irgendwo in der Funktion einen Namen lokal macht, und den UnboundLocalError beheben
  1. Aufwärmen · Aufgabe 1 von 7

    Aufwärmen aus Modul B3: Eine Funktion weist in ihrem Rumpf total = 0 zu. Welche Aussagen stimmen? Wählen Sie alle zutreffenden.

    Wählen Sie alle zutreffenden aus.

  2. Vorhersagen · Aufgabe 2 von 7

    Sagen Sie es vorher, bevor Sie weiterlesen: drei Funktionen, ein Name. Was gibt das aus?

    x = "global"
    
    
    def outer():
        x = "enclosing"
    
        def inner():
            return x
    
        return inner()
    
    
    print(outer(), x)
  3. Üben · Aufgabe 3 von 7

    Setzen Sie das Schlüsselwort ein, damit step() das count von make_counter ändert und jeder Aufruf die nächste Zahl liefert.

    def make_counter():
        count = 0
    
        def step():
            ____ count
            count += 1
            return count
    
        return step
    count
  4. Üben · Aufgabe 4 von 7

    Diesmal ohne global-Anweisung. Was gibt das aus?

    names = []
    
    
    def add(name):
        names.append(name)
    
    
    add("ada")
    add("bob")
    print(names)
  5. Üben · Aufgabe 5 von 7

    Ordnen Sie jeder Zeile, die in einer Funktion steht, ihre Wirkung zu.

  6. Denksport · Aufgabe 6 von 7

    Knobelaufgabe. Das print steht vor der Zuweisung. Was passiert, wenn show() läuft?

    total = 10
    
    
    def show():
        print(total)
        total = 20
    
    
    show()
  7. Anwenden · Aufgabe 7 von 7

    Mini-Aufgabe. Schreiben Sie make_averager(), das eine Funktion add(value) zurückgibt. Jeder Aufruf von add liefert den Durchschnitt aller bisher übergebenen Werte: 10, dann 20, dann 60 ergeben 10.0, 15.0 und 30.0. Halten Sie Summe und Anzahl in make_averager, nicht in globalen Variablen.

    Prüfen Sie Ihr Ergebnis anhand dieser Liste

Selbst programmieren

Lesen Sie das ausgearbeitete Beispiel und lösen Sie dann die Übungen. Ihr Code läuft in Ihrem Browser oder auf Ihrem Computer und wird nie hochgeladen.

Ausgearbeitetes Beispiel

Ein Debug-Schalter und Ticketzähler

Drei Wege, wie eine Funktion einen Namen außerhalb ihrer selbst erreicht. enable_debug und log binden Modulnamen mit global neu. make_counter gibt eine Closure zurück, die ihren eigenen Zähler mit nonlocal führt, sodass sich zwei Zähler keine Zahl teilen. Der letzte log-Aufruf liest len, das Python im eingebauten Scope findet.

main.py

from collections.abc import Callable

# Three ways a function reaches a name outside itself.
DEBUG = False
calls = 0


def enable_debug() -> None:
    global DEBUG
    DEBUG = True


def log(message: str) -> None:
    global calls
    calls += 1
    if DEBUG:
        print(f"[{calls}] {message}")


def make_counter(start: int) -> Callable[[], int]:
    count = start

    def step() -> int:
        nonlocal count
        count += 1
        return count

    return step


log("not shown: debug is off")
enable_debug()
log("debug is on")

tickets = make_counter(100)
orders = make_counter(0)
print(tickets(), tickets(), orders())
log(f"len is still the built-in: {len('abc')}")
print("calls:", calls)

Ausführen mit

python main.py

Ausgabe

[2] debug is on
101 102 1
[3] len is still the built-in: 3
calls: 3
  • Der erste log-Aufruf wird gezählt, gibt aber nichts aus; die erste sichtbare Zeile ist daher [2].
  • log liest DEBUG ohne Deklaration: Zum Lesen eines globalen Namens braucht es kein global.
  • tickets und orders haben je ihr eigenes count, denn jeder Aufruf von make_counter legt ein neues an.
  • Callable[[], int] ist der Typ einer Funktion ohne Argumente, die ein int zurückgibt.
Ändern und ausführen

Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.

Beim ersten Ausführen lädt Ihr Browser Python herunter (bis zu 6.5 MB) und speichert es im Cache. Ihr Code bleibt auf Ihrem Gerät.

Übungen

Übung 1 von 2

Ein Seitenaufruf-Zähler

record(page) soll views auf Modulebene um eins erhöhen, die Seite an history anhängen und den neuen Stand von views zurückgeben. reset() soll views auf 0 setzen und history leeren. Beide Funktionen sind kaputt: record löst UnboundLocalError aus, und reset lässt views unverändert. Reparieren Sie sie mit der richtigen Deklaration und lassen Sie history.append und history.clear, wie sie sind.

Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.

Beim ersten Ausführen lädt Ihr Browser Python herunter (bis zu 6.5 MB) und speichert es im Cache. Ihr Code bleibt auf Ihrem Gerät.

Hinweise
  1. Hinweis 1

    Beide Funktionen weisen views etwas zu, also behandelt Python views in beiden als lokal.

  2. Hinweis 2

    Schreiben Sie global views als erste Zeile in record und in reset.

  3. Hinweis 3

    history braucht keine Deklaration: append und clear ändern die Liste, ohne den Namen neu zu binden.

Eine Lösung zeigen

Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.

views = 0
history: list[str] = []


def record(page: str) -> int:
    """Count one view of page and return the total so far."""
    global views
    views += 1
    history.append(page)
    return views


def reset() -> None:
    """Set the count back to 0 and forget the history."""
    global views
    views = 0
    history.clear()
Auf dem eigenen Computer ausführen

Installieren Sie Python 3.14 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.

main.py

views = 0
history: list[str] = []


def record(page: str) -> int:
    """Count one view of page and return the total so far."""
    views += 1
    history.append(page)
    return views


def reset() -> None:
    """Set the count back to 0 and forget the history."""
    views = 0
    history.clear()

test_main.py

import main
from main import record, reset


def test_record_counts():
    """record liefert nach einem reset erst 1, dann 2"""
    reset()
    got = record("/home"), record("/about")
    assert got == (1, 2), f"record lieferte {got!r}, erwartet: (1, 2)"


def test_module_count():
    """Auch views auf Modulebene ändert sich"""
    reset()
    record("/home")
    assert main.views == 1, f"main.views ist nach einem record {main.views!r}, erwartet: 1"


def test_history():
    """record merkt sich die Seiten in ihrer Reihenfolge"""
    reset()
    record("/a")
    record("/b")
    assert main.history == ["/a", "/b"], f"main.history ist {main.history!r}, erwartet: ['/a', '/b']"


def test_reset():
    """reset setzt views auf 0 und leert history"""
    reset()
    record("/a")
    reset()
    assert main.views == 0, f"main.views ist nach reset {main.views!r}, erwartet: 0"
    assert main.history == [], f"main.history ist nach reset {main.history!r}, erwartet: []"

Unter macOS und Linux tippen Sie python3, wo in diesen Befehlen python steht, wie in der ersten Lektion.

Programm ausführen:

python main.py

Prüfungen ausführen (learnrun.py muss im selben Ordner liegen):

python learnrun.py test
learnrun.py herunterladen

Übung 2 von 2

Ein Ratenbegrenzer

make_limiter(limit) gibt eine Funktion allow() zurück. allow() liefert True für die ersten limit Aufrufe und False für jeden weiteren. Jeder Begrenzer zählt für sich: Zwei Begrenzer dürfen sich keinen Zähler teilen. Halten Sie den Zähler in make_limiter und ändern Sie ihn in allow mit nonlocal. Keine globalen Variablen.

Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.

Beim ersten Ausführen lädt Ihr Browser Python herunter (bis zu 6.5 MB) und speichert es im Cache. Ihr Code bleibt auf Ihrem Gerät.

Hinweise
  1. Hinweis 1

    allow muss used ändern, und used gehört zu make_limiter, der umschließenden Funktion.

  2. Hinweis 2

    Beginnen Sie allow mit nonlocal used. Vergleichen Sie dann used mit limit.

  3. Hinweis 3

    if used >= limit: return False. Sonst erhöhen Sie used um eins und geben True zurück.

Eine Lösung zeigen

Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.

from collections.abc import Callable


def make_limiter(limit: int) -> Callable[[], bool]:
    used = 0

    def allow() -> bool:
        nonlocal used
        if used >= limit:
            return False
        used += 1
        return True

    return allow
Auf dem eigenen Computer ausführen

Installieren Sie Python 3.14 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.

main.py

from collections.abc import Callable


def make_limiter(limit: int) -> Callable[[], bool]:
    used = 0

    def allow() -> bool:
        # Return True for the first `limit` calls, False after that.
        return True

    return allow

test_main.py

from main import make_limiter


def test_first_calls_allowed():
    """Die ersten drei Aufrufe mit Limit 3 sind erlaubt"""
    allow = make_limiter(3)
    got = [allow() for _ in range(3)]
    assert got == [True, True, True], f"die ersten drei Aufrufe lieferten {got!r}"


def test_then_refused():
    """Aufrufe nach dem Limit werden abgelehnt"""
    allow = make_limiter(2)
    got = [allow() for _ in range(4)]
    assert got == [True, True, False, False], f"vier Aufrufe mit Limit 2 lieferten {got!r}"


def test_separate_counts():
    """Zwei Begrenzer zählen getrennt"""
    a = make_limiter(1)
    b = make_limiter(1)
    got = a(), a(), b()
    assert got == (True, False, True), f"a(), a(), b() lieferten {got!r}, erwartet: (True, False, True)"


def test_zero_limit():
    """Limit 0 lehnt schon den ersten Aufruf ab"""
    allow = make_limiter(0)
    got = allow()
    assert got is False, f"mit Limit 0 lieferte der erste Aufruf {got!r}"

Unter macOS und Linux tippen Sie python3, wo in diesen Befehlen python steht, wie in der ersten Lektion.

Programm ausführen:

python main.py

Prüfungen ausführen (learnrun.py muss im selben Ordner liegen):

python learnrun.py test
learnrun.py herunterladen

Häufige Fehler

Eine globale Variable ändern, ohne sie zu deklarieren

count = 0


def increment():
    count += 1


increment()

Was Python ausgibt

UnboundLocalError: cannot access local variable 'count' where it is not associated with a value

Warum, und die Lösung

count += 1 ist eine Zuweisung, also ist count in increment lokal, und das lokale count hat keinen Wert, zu dem man addieren könnte. Meinen Sie wirklich das count auf Modulebene, schreiben Sie global count als erste Zeile der Funktion. Oft besser: den Wert übergeben und den neuen zurückgeben, count = increment(count).

nonlocal für einen Modulnamen

total = 0


def add(n):
    nonlocal total
    total += n

Was Python ausgibt

SyntaxError: no binding for nonlocal 'total' found

Warum, und die Lösung

nonlocal sucht nur in umschließenden Funktionen, und add steht in keiner anderen Funktion: total ist global. Python weist die Datei zurück, bevor irgendetwas läuft. Nehmen Sie global total für einen Modulnamen und nonlocal für Closures.

Einen eingebauten Namen mit einem eigenen verdecken

list = [3, 1, 2]
letters = list("abc")

Was Python ausgibt

TypeError: 'list' object is not callable

Warum, und die Lösung

Der globale Name list zeigt jetzt auf Ihre Liste, und der globale Scope wird vor den eingebauten Namen durchsucht, also versucht list("abc"), eine Liste aufzurufen. Wählen Sie einen anderen Namen, etwa numbers. Dasselbe passiert mit str, dict, sum, max und id. In einer interaktiven Sitzung entfernt del list Ihren globalen Namen, und das eingebaute list ist wieder sichtbar.

Python im Browser: Pyodide 314.0.7, MPL-2.0. Lizenz und Quellcode

Abschlussquiz

5 Fragen, ohne Hinweise. Ab 80 % ist die Lektion abgeschlossen.

Erledigen Sie zuerst alle Aufgaben oben, um das Abschlussquiz freizuschalten.

Problem melden

Etwas ist falsch oder unklar? Beschreiben Sie es kurz, dann wird es geprüft und korrigiert.

#

Mindestens 20 Zeichen.

Nur, wenn Sie eine Antwort wünschen.

Kernideen

Namensräume und die LEGB-Suche

Ein Namensraum ordnet Namen Objekten zu. Ein Modul hat einen für seine globalen Namen, jeder Funktionsaufruf bekommt einen frischen für seine lokalen Namen, und die eingebauten Namen wie len und print liegen in einem weiteren. Liest Code einen Namen, sucht Python in fester Reihenfolge: Local (die aktuelle Funktion), Enclosing (äußere Funktionen, die nächste zuerst), Global (das Modul), dann Built-in. Der erste Treffer gewinnt: Ein lokaler Name verdeckt einen gleichnamigen globalen, ein globales len das eingebaute. Maßgeblich ist, wo der Code steht, nicht, von wo er aufgerufen wird.

global und nonlocal binden äußere Namen neu

Eine Zuweisung in einer Funktion erzeugt einen lokalen Namen und lässt den äußeren in Ruhe. Um einen Modulnamen neu zu binden, deklarieren Sie ihn zuerst: global count. Für einen Namen der umschließenden Funktion schreiben Sie nonlocal count; so behält eine Closure einen Zähler zwischen Aufrufen. nonlocal sucht nur in umschließenden Funktionen, nie im Modul. Um ein veränderbares Objekt zu ändern, braucht es keins von beiden: items.append(x) ändert die Liste, ohne items neu zu binden. Setzen Sie global sparsam ein: Werte übergeben und zurückgeben hält Funktionen testbar.

Eine Zuweisung irgendwo macht den Namen lokal

Python legt beim Übersetzen einer Funktion fest, welche Namen lokal sind, nicht Zeile für Zeile. Weist der Rumpf x irgendwo etwas zu, mit =, +=, einer for-Schleife oder einem import, ist x in der ganzen Funktion lokal, auch oberhalb dieser Zeile. Wird es gelesen, bevor es einen Wert hat, folgt UnboundLocalError: cannot access local variable 'x' where it is not associated with a value. UnboundLocalError ist eine Unterklasse von NameError. Abhilfe: eine global- oder nonlocal-Deklaration, wenn Sie den äußeren Namen meinen, sonst ein anderer lokaler Name.

Quellen

Zuletzt geprüft am 29. September 2026