Scopes#

Fragt man Personen in Deutschland nach dem Namen “Angela Merkel”, dann wird fast jede Person sofort wissen, wer mit diesem Namen gemeint ist - egal wo man in Deutschland ist. Fragt man aber jemanden nach dem Namen “Katja Oldenburg-Schmidt”, dann weiß fast niemand etwas mit diesem Namen anzufangen - außer in Buxtehude.

Angela Merkel ist fast allen Leuten in Deutschland ein Begriff, weil sie Bundeskanzlerin ist und der Gültigkeitsberich ihres Amtes sich auf ganz Deutschland bezieht. Katja Oldenburg-Schmidt ist hingegen die Bürgermeisterin der Stadt Buxtehude, der “Gültigkeitsbereich” ihres Amtes liegt nur im Bereich Buxtehude. Deswegen kennt man sie auch nur dort.

Auch Variablen in Python haben bestimmte Gültigkeitsbereiche. Jede Variablendefinition gilt jeweils in dem Bereich (auch Scope genannt), in dem diese Zuweisung auch definiert wurde. Bisher haben wir davon nichts gemerkt, weil wir uns immer im allumfassenden, globalen Gültigkeitsbereich befunden haben. Wir haben bisher nur sogenannte globale Variablen, vergleichbar mit Angela Merkel, definiert. Man kennt sie überall (in Deutschland).

Alle Variablen, die innerhalb von Funktionen definiert werdem, sind jedoch sogenannte lokale Variablen, die dann auch nur innerhalb dieser Funktion “bekannt” sind, so wie Katja Oldenburg-Schmidt nur in Buxtehude bekannt ist. Definieren wir innerhalb von Funktionen eine Variable, dann können wir sie innerhalb der Funktion unter diesem Namen aufrufen. Außerhalb der Funktion kennt man sie jedoch nicht und kann sie daher auch nicht aufrufen/verwenden.

Während man lokale Variablen nicht außerhalb des Gültigkeitsbereichs der Funktion kennt, kenn man globale Variablen überall und in jeder Funktion - auch wenn wir diese nicht explizit als Input eingegeben haben.

Schauen wir uns das mal in Python an. Im Folgenden definiere ich innerhalb einer Funktion namens buxtehude() die Variable bürgermeisterin und weise dieser den Wert "Oldenburg-Schmidt" zu.

def buxtehude():
    bürgermeisterin = "Oldenburg-Schmidt"

Nun führe ich die Funktion buxtehude() aus und versuche danach auf die in der Funktion buxtehude() definierte Variable bürgermeisterin zuzugreifen:

buxtehude()
bürgermeisterin
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-36-46ea971ab0d2> in <module>
----> 1 bürgermeisterin

NameError: name 'bürgermeisterin' is not defined

Obwohl die Variable bürgermeisterin innerhalb der Funktion buxtehude() definiert wurde, ist sie auch nach der Ausführung der Funktion nicht bekannt. Das liegt daran, dass die Variable innerhalb des lokalen Gültigkeitsbereiches (Scope) der Funktion buxtehude() definiert wurde und nicht außerhalb der Funktion im globalen Gültigkeitsbereiches.

Möchte man den Wert der lokalen, funktionsinternen Variable bürgermeisterin in den globalen Gültigkeitsbereich “nach draußen schaffen”, dann müssen wir die entsprechende Variable über das Schlüsselwort return explizit als Funktions-Output definieren und dann bei Funktionsausführung optional einer (globalen) Variable außerhalb der Funktion zuweisen:

def buxtehude():
    bürgermeisterin = "Oldenburg-Schmidt"
    return bürgermeisterin

bürgermeisterin_of_buxtehude = buxtehude()
bürgermeisterin_of_buxtehude
'Oldenburg-Schmidt'

Andersherum können wir innerhalb eines lokalen, funktionsinternen Gültigkeitsbereiches immer auch auf die Variablen des übergeordneten, globalen Gültigkeitsbereiches lesend zugreifen. Unten definiere ich eine Funktion, die intern auf die global definierte Variable kanzlerin zugreift und in die Konsole printet:

kanzlerin = "Merkel"

def buxtehude():
    print(kanzlerin)

buxtehude()
Merkel

Versuchen wir die Variable bundeskanzlerin innerhalb von buxtehude() zu überschreiben, verändern wir nicht die globale Variable kanzlerin, sondern definieren nun eine gleichnamige, funktionsinterne Variable kanzlerin.

Im untigen Beispiel definiere ich im globalen Gültigkeitsbereich die Variable kanzlerin und weise dieser den Wert "Merkel" zu. Innerhalb des lokalen Gültigkeitsbereiches der Funktion buxtehude weise ich der Variable kanzlerin den Wert "Baerbock" zu. Diese Neuzuweisung gilt aber nur innerhalb der Funktion buxtehude, wie man im Folgenden sieht.

kanzlerin = "Merkel"

def buxtehude():
    kanzlerin = "Baerbock"
    print(kanzlerin)

Unten wird die Funktion buxtehude ausgeführt, welche den Wert der Variable kanzlerin printet. Da der Wert der Variable kanzlerin innerhalb der Funktion auf "Baerbock" verändert wurde, wird entsprechend "Baerbock" geprintet.

buxtehude()
Baerbock

An der globalen Variable kanzlerin hat diese funktionsinterne Neudefinition allerdings nichts verändert:

kanzlerin
'Merkel'

Veränderbare Objekte global durch Funktion ändern#

Auch wenn globale Variablen innerhalb von Funktionen nicht ohne Weiteres neudefiniert werden können (es gibt tatsächlich eine Möglichkeit, welche wir an dieser Stelle jedoch nicht betrachten, weil nicht wichtig für uns), können veränderbare Objekte, welche durch die globalen Variablen bezeichnet werden, dennoch direkt und global innerhalb einer Funktion verändert werden.

Diese Eigenschaft kann am Anfang etwas verwirrend sein und zu teils ungewollten Nebeneffekten von Funktionen führen. Dennoch kann diese Eigenschaft für ABM teilweise relativ praktisch sein, weil wir so z.B. Agenten, welche als veränderbare Objekte dargestellt werden, über Funktionen direkt verändern können.

Unten definiere ich die Funktion opinion_blender(), welche die Meinung zweier als dict repräsentierter Agenten in den Mittelwert der beiden eingegebenen Agenten verändert. Die Veränderung der Agenten findet zwar innerhalb der Funktion statt, hat aber dennoch Auswirkungen auf der globalen Ebene.

# Funktion zur Veränderung der Meinung der Agenten definieren
def opinion_blender(agent1, agent2):
    new_opinion = (agent1["opinion"] + agent2["opinion"]) / 2
    agent1["opinion"] = new_opinion
    agent2["opinion"] = new_opinion
# Agenten definieren
agent1 = {"opinion": 0}
agent2 = {"opinion": 10}
# Funktion auf Agenten anwenden
opinion_blender(agent1, agent2)
# Agent 1 anschauen
agent1
{'opinion': 5.0}
# Agent 2 anschauen
agent2
{'opinion': 5.0}

Pro-Tipp

Scopes und deren Umsetzung in Python können in der Theorie etwas abstrakt und unintuitiv erscheinen. Hier empfiehlt es sich - wie eigentlich immer - einfach mal selbst mit Funktionsdefinitionen und global und lokal definierten Variablen herumzuexperimentieren. Dann bekommt man ein Gefühl dafür, wann welche Variable oder welches Objekt in welchem Gültigkeitsbereich existiert oder verändert werden kann.