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, ...).