Python: Wie verwendet man eine globale Variable in einer Funktion?
Eine Variable auf Modulebene innerhalb einer Funktion zu lesen braucht gar
nichts. Ihr etwas zuzuweisen braucht das Schlüsselwort global — und der
Fehler, den du ohne bekommst, zeigt auf die falsche Zeile. Genau das macht die
Sache verwirrend.
Lesen ist umsonst
zaehler = 0
def lies():
return zaehler
read_it() : 0
Python sucht im lokalen Namensraum, dann im umschließenden, dann im Modul, dann bei den eingebauten Namen. Eine Deklaration ist nicht nötig.
Eine Zuweisung erzeugt stattdessen eine lokale Variable
def weise_zu():
zaehler = 99
return zaehler
inside : 99 outside afterwards : 0
Die Funktion hat sich ihren eigenen zaehler gebaut, ihn benutzt und am Ende
weggeworfen. Der auf Modulebene hat sich nie verändert.
Das ist keine Sonderregel für globale Variablen, sondern die ganz gewöhnliche Regel, dass eine Zuweisung einen Namen im aktuellen Namensraum bindet.
Der Fehler, der es verrät
Und jetzt wird es interessant. Lies die Variable und weise ihr etwas zu:
def lies_dann_zuweisen():
print(zaehler) # Zeile 1
zaehler = 1 # Zeile 2
-> UnboundLocalError: cannot access local variable 'counter' where it is not associated with a value
Das Lesen ist fehlgeschlagen. Nicht das Schreiben.
Python entscheidet zur Übersetzungszeit, welche Namen lokal sind, und eine
einzige Zuweisung irgendwo im Rumpf macht den Namen für den gesamten Rumpf
lokal — auch für die Zeilen darüber. Wenn Zeile 1 läuft, ist zaehler also
bereits eine lokale Variable, der noch nichts zugewiesen wurde.
Deshalb sieht der Fehler so irreführend aus: Du bekommst ihn in einer Zeile, die nur liest, wegen einer Zeile weiter unten.
global
def mit_global():
global zaehler
zaehler = 99
after with_global() : 99
global sagt dem Compiler, den Namen nicht als lokal zu behandeln. Schreib es
an den Anfang der Funktion.
Oft brauchst du es gar nicht
Das ist der Teil, den man verinnerlichen sollte. Bei global geht es um das
Neubinden des Namens, nicht um das Verändern des Objekts. Verändern
funktioniert ohne:
elemente = []
register = {}
def veraendere():
elemente.append("x")
register["k"] = "v"
items after append : ['x']
registry after : {'k': 'v'}
Es findet keine Zuweisung an elemente oder register statt —
elemente.append(...) ist ein Methodenaufruf, und register["k"] = ... weist
einem Element zu und nicht dem Namen. Es gibt also nichts, was Python lokal
machen könnte.
Bindest du den Namen neu, brauchst du es wieder:
def binde_neu():
global elemente
elemente = ["neu"]
Das ist auch der übliche Ausweg: ein Dictionary oder ein kleines Objekt auf Modulebene halten und verändern, statt Modulnamen von überall her neu zu binden.
konfig = {"debug": False}
def debug_an():
konfig["debug"] = True
config after enable_debug() : {'debug': True}
nonlocal ist ein anderes Schlüsselwort
global springt gleich bis auf Modulebene. Für einen Namen in einer
umschließenden Funktion willst du
nonlocal:
def aussen():
summe = 0
def addiere(n):
nonlocal summe
summe += n
addiere(3); addiere(4)
return summe
outer() with nonlocal : 7
without nonlocal : UnboundLocalError: cannot access local variable 'total' ...
'global' does NOT reach the enclosing function : 0
Die dritte Zeile ist die bemerkenswerte. global in einer verschachtelten
Funktion erreicht die umschließende Funktion nicht — es erzeugt oder bindet
einen Namen auf Modulebene, und die umschließende Variable bleibt unberührt.
Module und die import-Form
Noch eine Stelle, an der das zubeißt. Diese beiden sind nicht gleichwertig:
from konfig import wert # kopiert die aktuelle Bindung
import konfig # danach konfig.wert lesen
from ... import bindet den Wert an einen Namen in deinem Modul. Bindet das
andere Modul ihn später neu, siehst du die Änderung nicht. import konfig mit
anschließendem konfig.wert liest jedes Mal das Attribut und liefert damit
immer den aktuellen Stand.
Sollte man?
Meistens nicht. Eine Funktion, die Zustand auf Modulebene liest und schreibt, ist schwerer zu testen, lässt sich nicht gefahrlos zweimal aufrufen, und ihre Signatur verrät nicht, was sie anfasst.
Übergib Werte hinein und gib sie zurück. Wo geteilter Zustand wirklich nötig
ist — ein Cache, ein Verbindungspool, ein Konfigurationsobjekt — ist ein
Container auf Modulebene, den du veränderst, ehrlicher und kommt ganz ohne
global aus.
Hinweis zu Netcup (Werbung)
Der deutsche Hoster Netcup bietet unter anderem günstige und zugleich leistungsstarke Webhosting Pakete, KVM-basierte Root Server und dezidierte Server an. Mit unseren Gutscheincodes kannst du noch mehr Geld sparen (6€ bei deiner ersten Bestellung, 30% Rabatt auf alle KVM-basierten Root Server, ...).