2.16. Gültigkeitsbereich¶
Wenn Python einen Namen innerhalb einer Funktion nachschlägt, durchsucht es eine bestimmte Abfolge von Gültigkeitsbereichen. Das Verständnis dieser Abfolge erklärt, warum einige Zuweisungen äußere Namen überdecken, warum andere sie verändern und warum verschachtelte Funktionen sich Werte von dort merken können, wo sie definiert wurden.
Die Namenssuche beginnt im lokalen Funktions-Gültigkeitsbereich und verläuft nach außen zu Modul- und eingebauten Gültigkeitsbereichen, bis eine Übereinstimmung gefunden wird.¶
2.16.1. Lokaler und Modul-Gültigkeitsbereich¶
Namen, die innerhalb einer Funktion definiert werden, sind lokal zu dieser Funktion und verschwinden, wenn der Aufruf endet:
def f():
x = 10
print(x)
f()
print(x) # NameError: x is not defined
Namen, die auf der obersten Ebene einer .py-Datei definiert werden, sind modulweit (manchmal auch global genannt) und überall in dieser Datei sichtbar, einschließlich innerhalb von Funktionen:
CAMERA = "OpenMV"
def banner():
print("running on", CAMERA)
Das Lesen eines modulweiten Namens von innerhalb einer Funktion erfolgt automatisch. Das Zuweisen zu diesem Namen von innerhalb einer Funktion jedoch nicht – die Zuweisung erzeugt eine neue lokale Variable, die die modulweite für den Rest des Aufrufs überdeckt:
counter = 0
def bump():
counter = counter + 1 # UnboundLocalError
Das counter auf der linken Seite macht counter zu einem lokalen Namen in bump, sodass der Lesevorgang auf der rechten Seite keinen Wert findet.
2.16.1.1. Das Schlüsselwort global¶
Um einen modulweiten Namen tatsächlich von innerhalb einer Funktion neu zuzuweisen, deklarieren Sie ihn zuerst als global:
counter = 0
def bump():
global counter
counter += 1
Greifen Sie sparsam auf global zurück. Funktionen, die verborgenen Zustand verändern, sind schwerer nachzuvollziehen als Funktionen, die Werte als Argumente entgegennehmen und neue Werte zurückgeben. Die übliche Lösung für „Ich muss Zustand teilen“ besteht darin, ein Objekt (eine Liste, ein Dict, eine Klasseninstanz) als Argument zu übergeben und dieses stattdessen zu verändern.
2.16.2. Lambdas¶
Ein lambda baut eine kleine anonyme Funktion in einem einzigen Ausdruck:
square = lambda x: x * x
square(7) # 49
Es ist exakt gleichbedeutend mit
def square(x):
return x * x
Der Rumpf eines lambda muss ein einzelner Ausdruck sein – keine Anweisungen, keine mehreren Zeilen. Die Hauptverwendung ist die Übergabe einer winzigen Funktion als Argument an etwas, das eine Funktion entgegennimmt:
pairs = [("b", 2), ("a", 3), ("c", 1)]
pairs.sort(key=lambda item: item[1])
# [('c', 1), ('b', 2), ('a', 3)]
Wenn der Rumpf über einen Ausdruck hinauswächst, wechseln Sie zu einem echten def. Das Benennen einer Funktion mit def gibt ihr außerdem einen Namen in Tracebacks, den ein lambda nicht hat.
2.16.3. Closures¶
Eine innerhalb einer anderen Funktion definierte Funktion kann Namen aus dem Gültigkeitsbereich der umgebenden Funktion lesen. Die innere Funktion fängt diese Namen ein und funktioniert weiter, selbst nachdem der äußere Aufruf zurückgekehrt ist:
def make_adder(n):
def add(x):
return x + n
return add
add5 = make_adder(5)
add10 = make_adder(10)
print(add5(100), add10(100))
Ausgabe:
105 110
add5 und add10 sind zwei separate Funktionen, von denen sich jede ihr eigenes n merkt. Eine Funktion, die auf diese Weise eine angepasste innere Funktion baut und zurückgibt, wird Closure genannt. Sie ist der Hauptgrund, warum eine Sprache überhaupt verschachtelte Funktionen benötigt – eine Möglichkeit, etwas Zustand in einen Funktionswert einzubacken und diesen Wert dann als einzelnes aufrufbares Objekt weiterzugeben.
Das Lesen eingefangener Namen geschieht automatisch. Das Neuzuweisen eines solchen erfordert ein zusätzliches Schlüsselwort. Das folgende Beispiel tut, was richtig aussieht, und scheitert:
def make_counter():
count = 0
def tick():
count = count + 1 # UnboundLocalError
return count
return tick
Die Zuweisung an count innerhalb von tick macht count lokal zu tick, genauso wie sie es in einer Funktion auf oberster Ebene lokal gemacht hätte. Das Schlüsselwort nonlocal teilt Python mit: „Dieser Name lebt in der umgebenden Funktion, weise ihn dort neu zu“:
def make_counter():
count = 0
def tick():
nonlocal count
count += 1
return count
return tick
c = make_counter()
print(c(), c(), c())
Ausgabe:
1 2 3
nonlocal verhält sich zum Gültigkeitsbereich der umgebenden Funktion so wie global zum Modul-Gültigkeitsbereich. Beachten Sie, dass das Verändern eines eingefangenen Objekts (Aufruf von some_list.append(...), some_dict[k] = v) kein nonlocal benötigt – der Name wird nicht neu zugewiesen, sondern nur das Objekt, auf das er zeigt, wird verändert.