Python-Kurs / Gültigkeitsbereiche, gemeinsame Referenzen und veränderliche Objekte verstehen
Der Gültigkeitsbereich bestimmt, wo Python einen Namen nachschlägt; die Veränderlichkeit bestimmt, welche Änderungen über gemeinsam referenzierte Objekte sichtbar werden. Eine neue Zuweisung an einen lokalen Namen und eine Änderung der bereits referenzierten Liste sind deshalb zwei verschiedene Vorgänge.
Lokale Namen und äußere Bindungen unterscheiden
Ein Name verweist auf ein Objekt. Eine Zuweisung innerhalb einer gewöhnlichen Funktion erzeugt normalerweise eine lokale Bindung, sofern keine passende global- oder nonlocal-Deklaration vorliegt. Das Lesen einer äußeren Bindung ist davon zu unterscheiden: Für das gewöhnliche Nachschlagen in Funktionskörpern hilft die Reihenfolge Local, Enclosing, Global, Builtins, kurz LEGB. Gemeint sind der aktuelle Funktionsbereich, umgebende Funktionen, das Modul und die eingebauten Namen. Dies ist eine Orientierung für normale Funktionen, keine vollständige Beschreibung aller besonderen Klassen- und Annotationsbereiche.
global bezieht eine Bindung auf das Modul, nonlocal auf eine bereits bestehende Bindung in einer umgebenden Funktion. Diese Deklarationen kopieren kein Objekt. Prüfen Sie vor ihrer Verwendung, ob explizite Parameter und Rückgabewerte den Datenfluss verständlicher machen. Die Sprachreferenz zur Namensauflösung und die Einführung zu Namensräumen beschreiben die Regeln.
Gemeinsam verwendete und kopierte Listen beobachten
Speichern Sie den vollständigen Code als scope.py in UTF-8 im Übungsordner. Starten Sie python scope.py mit dem Projektinterpreter; außerhalb einer aktivierten Umgebung kann unter macOS oder Ubuntu python3 passen. Zusatzpakete und Eingabedateien sind nicht nötig.
def add_fee(items, fee):
items.append(fee)
original = [3500, 8000]
alias = original
copy = original.copy()
add_fee(alias, 500)
print(original)
print(copy)
nested = [[1], [2]]
shallow = nested.copy()
shallow[0].append(9)
print(nested)
Erwartete Ausgabe:
[3500, 8000, 500]
[3500, 8000]
[[1, 9], [2]]
alias = original erzeugt keine zweite Liste. Beide Namen verweisen auf dasselbe Objekt, weshalb add_fee(alias, 500) die auch über original sichtbare Liste ändert. Der Parameter items ist zwar ein lokaler Name, verweist in diesem Aufruf aber ebenfalls auf dieses Objekt. Dafür ist keine global-Deklaration erforderlich.
original.copy() erzeugt eine neue äußere Liste. Das Anhängen an die eine äußere Liste hängt nichts an die andere an. Die Namen copy und shallow sind hier nur Variablennamen. Im verschachtelten Beispiel teilen sich nested und shallow jedoch weiterhin die inneren Listen. Deshalb ist die angehängte 9 über nested[0] sichtbar.
Lokale Zuweisung und einen nonlocal-Zähler verfolgen
Lassen Sie das erste Beispiel unverändert und speichern Sie dieses zweite vollständige Experiment als name_scopes.py. Führen Sie python name_scopes.py im selben Ordner aus. Der getrennte Dateiname verhindert, dass Sie die erste Übung überschreiben.
value = 10
def local_read():
value = 20
return value
def broken():
value += 1
return value
print(local_read(), value)
try:
broken()
except UnboundLocalError:
print("local name not initialized")
def make_counter():
count = 0
def increment():
nonlocal count
count += 1
return count
return increment
counter = make_counter()
print(counter(), counter())
Erwartete Ausgabe:
20 10
local name not initialized
1 2
local_read setzt einen eigenen lokalen Wert auf 20; der Modulwert bleibt 10. In broken ist value aufgrund der Zuweisung ebenfalls lokal. value += 1 muss den bisherigen lokalen Wert lesen, bevor es ihn erhöhen kann. Da dieser noch nicht gebunden ist, entsteht UnboundLocalError. Der später im Quelltext stehende Zuweisungsvorgang lässt Python nicht zuvor den Modulwert verwenden.
make_counter erzeugt dagegen einen eingeschlossenen Zustand. Die innere Funktion erklärt count als nonlocal und aktualisiert damit die Bindung der umgebenden Funktion. Beide Aufrufe desselben zurückgegebenen Zählers liefern nacheinander 1 und 2. Ein erneuter Aufruf von make_counter() erstellt einen eigenen Zählerzustand; zwei getrennte Zähler müssen sich daher nicht gegenseitig erhöhen.
Eine tiefe Kopie über copy.deepcopy kann verschachtelte Daten rekursiv kopieren, ist aber kein allgemeiner Mechanismus zum Duplizieren offener Dateien oder Verbindungen. Außerdem kann beabsichtigt geteilte Struktur dadurch verändert werden. Wenn Sie für ein separates Experiment import copy verwenden, beachten Sie, dass die Variable copy aus dem ersten Beispiel denselben Namen bereits belegt. Wählen Sie etwa import copy as copy_module und copy_module.deepcopy(nested). Die Dokumentation zum Kopieren von Objekten beschreibt die Möglichkeiten und Grenzen.
Änderungen als Teil des Funktionsvertrags prüfen
- Fügen Sie im ersten Beispiel nach dem bisherigen Aufruf
add_fee(copy, 100)ein. Die kopierte Liste enthält nun 100;originalerhält weiterhin nur die zusätzliche 500. Das prüft die Unabhängigkeit der äußeren Listen. - Ersetzen Sie innerhalb von
add_feenuritems.append(fee)durchitems = items + [fee]. Jetzt entsteht eine neue Liste, an die nur der lokale Name gebunden wird.originalundaliasbleiben bei ihren ursprünglichen zwei Beträgen. Die Funktion liefert weiterhin implizitNone; sie gibt die neue Liste nicht zurück. - Soll eine Funktion das Original bewahren und die erweiterte Liste bereitstellen, kann ihr Körper stattdessen
return items + [fee]enthalten. Speichern Sie das Ergebnis beim Aufrufer unter einem eigenen Namen und prüfen Sie Ergebnis und Original getrennt. - Stellen Sie das ursprüngliche
appendwieder her und vergleichen Sie alle drei Ausgabezeilen. Kontrollieren Sie mitalias is originaldie gemeinsame Identität und mitshallow[0] is nested[0]die gemeinsame innere Liste.
Beschreiben Sie bei einer Funktion, ob sie übergebene Objekte verändert oder unabhängige Ergebnisse erzeugt. Eine richtige Summe oder Rückgabe allein beweist nicht, dass die Originaldaten unangetastet bleiben. Die Übung zu Standardwerten und geteilten Listen zeigt dieselbe Unterscheidung zwischen neuen Aufrufen und gemeinsam verwendeten Objekten aus einer anderen Perspektive.




