mitmario.dev

Gültigkeitsbereich

Python Sandbox 5 Min Lesezeit 6 BeispieleLektion 5 von 9

Jede Funktion in diesem Abschnitt hat eigene Variablen, text in euro, preis in kaffee, rein und raus in bilanz. Außerhalb ihrer Funktion hat keine davon je eine Rolle gespielt. Dahinter steckt eine Regel, der Gültigkeitsbereich (scope) einer Variablen. Das ist der Bereich im Code, in dem es sie gibt.

Was in der Funktion bleibt

Was in der Funktion bleibt
def brutto(netto):
    ergebnis = round(netto * 1.19, 2)
    return ergebnis


print(brutto(100))
print(ergebnis)

ergebnis bekommt in der Funktion seinen Wert, und return gibt diesen Wert nach draußen. Den Namen aber nicht. Die letzte Zeile endet mit NameError: name 'ergebnis' is not defined, als hätte es ergebnis nie gegeben. Und draußen hat es das auch nie.

Eine Variable, die in einer Funktion einen Wert bekommt, ist lokal (local). Es gibt sie nur in dieser Funktion, und nur, solange die Funktion läuft. Wenn sie fertig ist, ist der Name weg, und beim nächsten Aufruf fängt er von vorn an. Parameter sind auch lokal, netto gibt es draußen genauso wenig.

Was eine Funktion nach draußen geben will, gibt sie mit return zurück. Wo sie aufgerufen wird, bekommt der Wert dann einen eigenen Namen.

Das schützt die Funktion. Sie darf ergebnis, summe oder text nennen, was sie will, ohne nachzusehen, ob es draußen schon etwas mit diesem Namen gibt. Die Funktion bilanz nennt ihre Summen rein und raus, und ein rein draußen wäre ihr egal.

Lesen von draußen

Lesen von draußen
MWST = 19


def brutto(netto):
    return round(netto * (1 + MWST / 100), 2)


print(brutto(100))
print(brutto(4.20))

Umgekehrt geht etwas. MWST steht draußen, ganz oben in der Datei, und die Funktion liest es trotzdem. Wenn Python einen Namen in der Funktion nicht findet, sieht es draußen in der Datei nach. Namen, die dort stehen, heißen global (global), weil es sie in der ganzen Datei gibt.

Der Name in Großbuchstaben ist eine Abmachung unter Python-Leuten. So schreibt man Werte, die sich während des ganzen Programms nicht ändern, eine Konstante (constant). Python selbst ist das egal, du könntest MWST ändern. Aber jeder, der den Code liest, weiß, dass sich der Wert nicht ändern soll. Für eine Konstante ist Lesen von draußen genau richtig.

Ändern von draußen

Zwei Variablen mit einem Namen
kontostand = 100


def zuruecksetzen():
    kontostand = 0
    print("In der Funktion:", kontostand)


zuruecksetzen()
print("Draußen:", kontostand)

Beim Ändern ist es anders. Die Funktion setzt kontostand auf 0, und in der Funktion steht auch 0. Draußen stehen danach aber immer noch 100 Euro. Mit der Zuweisung in der Funktion wurde nicht der Kontostand draußen geändert. Es wurde eine neue, lokale Variable angelegt, die zufällig genauso heißt. Zwei Variablen, ein Name, und welche gemeint ist, hängt am Ort.

Im Reiter Debug kannst du das nachsehen. Wenn du das Beispiel aufzeichnest, steht draußen am Rand kontostand mit 100, sobald die erste Zeile gelaufen ist. Im ersten Schritt in der Funktion steht dort gar nichts, weil die Funktion noch keine eigenen Namen hat. Erst vor dem print taucht kontostand mit 0 auf. Die 100 von draußen siehst du in der Funktion nicht, dort stehen nur ihre eigenen Namen. Im letzten Schritt, wieder draußen, steht dann wieder die 100.

Lesen und ändern zugleich
kontostand = 100


def einzahlen(betrag):
    kontostand = kontostand + betrag


einzahlen(50)
print(kontostand)

Und hier wird es seltsam. Diese Funktion will den Kontostand von draußen lesen und gleich ändern. Das Programm endet mit UnboundLocalError: cannot access local variable 'kontostand' where it is not associated with a value. Auf Deutsch heißt das etwa, dass auf die lokale Variable kontostand nicht zugegriffen werden kann, weil sie noch keinen Wert hat.

Dabei steht draußen doch einer. Python entscheidet aber für die ganze Funktion auf einmal, ob ein Name lokal ist, und zwar bevor sie läuft. Steht irgendwo in der Funktion kontostand = …, ist kontostand dort überall lokal, auch rechts vom Gleichheitszeichen in derselben Zeile. Und diese lokale Variable soll gelesen werden, bevor sie ihren ersten Wert hat.

Die Meldung gehört zu den verwirrendsten, die Python hat. Fast immer heißt sie, dass eine Funktion versucht, eine Variable von draußen zu ändern.

Der Weg über global

Mit global
kontostand = 100


def einzahlen(betrag):
    global kontostand
    kontostand = kontostand + betrag


einzahlen(50)
print(kontostand)

Es gibt einen Weg, das trotzdem zu tun. Mit global kontostand in der ersten Zeile der Funktion sagst du Python, dass hier der kontostand von draußen gemeint ist, auch zum Ändern. Danach steht draußen 150.

In diesem Kurs kommt global nicht vor, und dafür gibt es einen praktischen Grund. Eine Funktion mit global hängt an einem Wert, der irgendwo anders steht und den jede andere Funktion genauso ändern darf. Wenn du wissen willst, warum der Kontostand am Ende falsch ist, musst du jede Funktion lesen, die ihn anfasst, und zwar in der Reihenfolge, in der sie aufgerufen wurden. Einzeln ausprobieren kannst du so eine Funktion auch nicht, denn jeder Aufruf ändert das echte Konto.

Hinein über Parameter, hinaus über return

Hinein und wieder hinaus
def einzahlen(stand, betrag):
    return stand + betrag


kontostand = 100
kontostand = einzahlen(kontostand, 50)
print(kontostand)
print(einzahlen(0, 20))
print(kontostand)

Der andere Weg ist der aus den Lektionen davor. Die Funktion bekommt den Stand als Parameter, rechnet und gibt den neuen Stand zurück. Draußen entscheidet eine einzige Zeile, ob sich der Kontostand ändert, nämlich kontostand = einzahlen(kontostand, 50). Wer den Code liest, sieht genau an dieser Zeile, dass er es tut.

Außerdem lässt sich die Funktion jetzt mit beliebigen Zahlen ausprobieren, ohne dass das echte Konto davon etwas merkt. einzahlen(0, 20) gibt 20 zurück, und kontostand steht danach immer noch bei 150 Euro. Eine Funktion, die nur über Parameter und return mit dem Rest des Programms zu tun hat, kannst du so für sich allein prüfen. Auch pruefen.py macht es seit Lektion 8.2 so und ruft deine Funktionen mit eigenen Werten auf, um nachzusehen, was zurückkommt.

Was die Aufgabe will

Ein kleines Programm führt ein Konto mit global und rechnet falsch. Die Funktion, die nur nachsehen soll, ob das Geld reicht, hebt dabei nämlich gleich ab. Die drei Funktionen sollen so umgebaut werden, dass sie den Stand bekommen und zurückgeben, und dann stimmt auch die Rechnung.

Zum Mitnehmen

Ein Name, der in einer Funktion entsteht, ist draußen unbekannt und mit dem Ende der Funktion weg. Namen von außerhalb kann sie ohne Weiteres lesen. Soll sich draußen etwas ändern, bekommt sie den Stand als Parameter und gibt den neuen mit return zurück.

Jetzt du

Basis Konto, kostenlos

Zu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.

Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.

In diesem Kurs läuft dein Code auf einem Server. Dafür hat das Basis Konto 1 Stunde im Monat, mehr Zeit gibt es mit dem Premium Konto.

Was in dieser Lektion steckt

  • Artikel mit 6 Beispielen zum Ausprobieren

    Steht hier, ohne Konto lesbar.

  • Aufgabe, dein Code läuft auf einem Server

    Öffnet sich mit dem Basis Konto.