Читать книгу Routineaufgaben mit Python automatisieren - Al Sweigart - Страница 90
Der Aufrufstack
ОглавлениеStellen Sie sich eines dieser Gespräche vor, in denen Sie vom Hundertsten ins Tausendste kommen. Beispielsweise wollen Sie eigentlich über Ihre Freundin Alice sprechen, was Sie an eine Geschichte über Ihren Kollegen Bob erinnert, die Sie aber nicht gleich erzählen können, weil Sie dazu erst einmal etwas über Ihre Cousine Carol berichten müssen. Wenn Sie mit den Hinweisen zu Carol fertig sind, kehren Sie zu der Geschichte über Bob zurück, und danach reden Sie wieder von Alice. Dabei aber werden Sie an Ihren Bruder David erinnert, weshalb Sie eine Bemerkung über ihn einschieben, bevor Sie endlich Ihre Geschichte über Alice abschließen. Ihre Äußerungen folgen dem Muster aus Abb. 3–1, in dem die Gesprächsthemen einen »Stapel« (Stack) bilden und das aktuelle Thema jeweils obenauf liegt.
Abb. 3–1 Der Stack einer Unterhaltung mit vielen Abschweifungen
Ebenso führt auch der Aufruf einer Funktion nicht dazu, dass die Ausführung wie eine Reise ohne Wiederkehr zum Anfang der betreffenden Funktion umgeleitet wird. Python merkt sich, in welcher Zeile die Funktion aufgerufen wurde, sodass die Ausführung nach dem Auftreten einer return-Anweisung dort fortgesetzt werden kann. Ruft die ursprüngliche Funktion weitere Funktionen auf, kehrt die Steuerung nach deren Verarbeitung zunächst zu dem Aufruf dieser verschachtelten Funktionen und erst dann zum Aufruf der ursprünglichen Funktion zurück.
Geben Sie im Dateieditor den folgenden Code ein und speichern Sie ihn als abcdCallstack.py:
def a():
print('a() starts')
b()
d()
print('a() returns')
def b():
print('b() starts')
c()
print('b() returns')
def c():
print('c() starts')
print('c() returns')
def d():
print('d() starts')
print('d() returns')
a()
Wenn Sie dieses Programm ausführen, erhalten Sie die folgende Ausgabe:
a() starts
b() starts
c() starts
c() returns
b() returns
d() starts
d() returns
a() returns
Die Ausführung dieses Programms können Sie sich auf https://autbor.com/abcdcallstack/ ansehen. Wird die Funktion a() aufgerufen (), so ruft sie ihrerseits b() auf (), die wiederum c() aufruft (). Die Funktion c() ruft nichts auf, sondern gibt lediglich die Zeilen c() starts () und c() returns aus, bevor die Steuerung zu der Zeile in b() zurückspringt, in der c() aufgerufen wurde (). Dort angekommen, springt die Steuerung zu der Zeile in a() zurück, in der b() aufgerufen wurde (). Die Ausführung geht jetzt mit der nächsten Zeile in a() weiter, nämlich mit dem Aufruf von d() (). Ebenso wie c() ruft auch d() nichts auf, sondern gibt lediglich d() starts und d() returns aus, bevor sie die Steuerung an die Zeile in a() zurückgibt, in der sie aufgerufen wurde. Die letzte Zeile in a()gibt a() returns aus und springt dann zu dem ursprünglichen Aufruf von a() am Ende des Programms zurück ().
Mithilfe des Aufrufstacks merkt sich Python, wohin die Steuerung nach einem Funktionsaufruf jeweils zurückspringen muss. Dieser Stack wird jedoch nicht in einer Variablen gespeichert, sondern hinter den Kulissen von Python gehandhabt. Wenn das Programm eine Funktion aufruft, erstellt Python ein Frameobjekt an der Spitze des Aufrufstacks. In Frameobjekten ist die Zeilennummer des ursprünglichen Funktionsaufrufs gespeichert, sodass sich Python merken kann, wohin es zurückspringen muss. Erfolgt ein weiterer Funktionsaufruf, legt Python im Stack ein weiteres Frameobjekt auf das vorhergehende.
Beim Rücksprung aus einer aufgerufenen Funktion entfernt Python das zugehörige Frameobjekt vom Stapel und fährt mit der Ausführung in der Zeile mit der gespeicherten Nummer fort. Frameobjekte werden dabei immer oben auf den Stapel gelegt und auch dort wieder von ihm heruntergenommen, niemals an einer anderen Stelle. Abb. 3–2 zeigt die verschiedenen Zustände des Aufrufstacks von abcdCallStack.py, während die einzelnen Funktionen aufgerufen werden und zurückspringen.
Abb. 3–2 Frameobjekte im Aufrufstack im Verlauf der Funktionsaufrufe und Rücksprünge in abcdCallStack.py
Das oberste Objekt auf dem Stack besagt, welche Funktion gerade ausgeführt wird. Wenn der Aufrufstack leer ist, erfolgt die Ausführung außerhalb von Funktionen.
Beim Aufrufstack handelt es sich um eine technische Hintergrundeinrichtung, die Sie nicht unbedingt kennen müssen, um Programme schreiben zu können. Es reicht zu wissen, dass Funktionsaufrufe zu der Zeile zurückkehren, von der aus sie aufgerufen wurden. Allerdings erleichtern Kenntnisse des Aufrufstacks das Verständnis der lokalen und globalen Gültigkeitsbereiche, die wir uns im folgenden Abschnitt ansehen.