Coroutinen sind im Grunde Funktionen, deren Ausführung an einem bestimmten Punkt angehalten / angehalten werden kann, und dann können wir die Ausführung von diesem Punkt später fortsetzen, wann immer wir wollen.

Wir benötigen einen Mechanismus — oder genauer gesagt ein Schlüsselwort —, mit dem wir einen Kontrollpunkt einfügen und dem Programm mitteilen können, dass wir die Ausführung der Funktion hier anhalten und die Kontrolle an den Punkt zurückgeben möchten, von dem aus sie aufgerufen wurde. Wir setzen die Hinrichtung fort, wann immer wir wollen.

In Python können wir die Ausführung einer Funktion mit dem Schlüsselwort yield anhalten.

Hier wird es also interessant:

  • Wir können uns eine Coroutine als eine Funktion vorstellen, die einen oder mehrere Checkpoints hat, an denen die Ausführung angehalten wird und die Steuerung an den Punkt zurückgegeben wird, von dem sie aufgerufen wurde.
  • Im Wesentlichen ist eine Coroutine eine Funktion, die in viele Teile unterteilt ist, und wir können jeden Teil einer Coroutine ausführen, während wir jede Iteration einer for-Schleife mit der Funktion next ausführen.

Hier ist ein grundlegendes Beispiel:

OUTPUT :<class 'generator'>
Function Starts
Function Ends

Von der Ausgabe bemerken wir ein paar Dinge:

  • Zuerst müssen wir die Coroutine / Funktion aufrufen, die uns ein Generatorobjekt gibt.
  • Dieses Generatorobjekt verhält sich ähnlich wie ein Iterator, aber im Fall eines Iterators durchlaufen wir ein Iterable . Mit einem Generator führen wir Teile der Coroutine aus.
  • So wie eine StopIteration -Ausnahme ausgelöst und hinter den Kulissen einer for-Schleife abgefangen wird, geschieht dies auch in diesem Fall, wenn der letzte Teil der Coroutine ausgeführt wird.

Nun ist dieses Pausieren der Funktion zwischendurch sehr interessant und eröffnet einige Möglichkeiten:

  • Wenn die Funktion angehalten wird, tun wir nichts, was wir gerade gesehen haben.
  • Angenommen, eine Variable wird in einer Funktion mehrmals geändert und wir möchten den Wert dieser bestimmten Variablen an einem bestimmten Prüfpunkt. Wenn wir diese Funktion an diesem bestimmten Prüfpunkt anhalten, gibt sie den Wert dieser Variablen zurück.

Sehen wir uns ein Beispiel an:

OUTPUT :Function Part 1
5
Function part 2
12
Function part 3

Hier wird der Wert von x von yield an verschiedenen Checkpoints zurückgegeben, da die Funktionsausführung angehalten wurde.

Immer wenn wir den letzten Teil der Funktion ausführen und die Funktion keinen Ertrag mehr enthält, wird nach dem Ausführen dieses letzten Teils eine StopIteration Ausnahme ausgelöst.

Ähnlich wie wenn ein Iterator versucht, die nächste Funktion auszuführen, aber keine Elemente mehr im Iterable vorhanden sind, wird auch die Ausnahme StopIteration ausgelöst.

  • Angenommen, wir möchten einen Wert (der eine Konstante oder Variable sein kann) an einem bestimmten Prüfpunkt (dh an einem bestimmten Zustand einer Funktion) senden. Wir können dies auch mit dem Schlüsselwort yield tun. Wenn wir einen Wert senden möchten, verwenden wir die Funktion send anstelle von next .

Sehen wir uns ein Beispiel an:

OUTPUT :Function part 1
6
Function part 2
12
Function part 3

Der Grund, warum wir next vor der Verwendung von send verwendet haben, ist, dass wir send nur verwenden können, wenn wir uns am Kontrollpunkt yield befinden und yield sich auf der rechten Seite des Ausdrucks befindet. Um diese erste yield zu erreichen, müssen wir die next -Funktion verwenden.

Hier kommt nun eine interessante Anwendung von Coroutinen. Angenommen, wir möchten zwischen zwei Funktionen hin und her wechseln, wie wir es beim Multithreading tun. In Multithreading, bis ein interrupt vom Betriebssystem angetroffen wird, wird es weiter ausgeführt. In diesem Fall können wir wechseln, wann immer wir wollen.

Sehen wir uns ein Beispiel an:

OUTPUT :Function 1 part 1
Function 2 part 1
Function 1 part 2
Function 1 part 3
Function 2 part 2
Function 2 part 3
Function 2 part 4
Function 1 part 4
Function 1 part 5

In diesem Beispiel können wir sehen, dass wir zwischen Coroutinen hin und her wechseln können, wann immer wir wollen.

Wenn wir also unseren eigenen benutzerdefinierten Scheduler schreiben, der das Umschalten zwischen mehreren Koroutinen übernimmt, können wir mit Single-Threading erreichen, was wir mit Multithreading tun.

Coroutinen haben viele Anwendungen wie Parallelität und andere Programmiermuster können ebenfalls implementiert werden, wie Produzent-Konsument oder Sender-Empfänger in der Netzwerkprogrammierung. Ich werde diese in den kommenden Artikeln untersuchen.

Coroutinen sind auch die Bausteine vieler Frameworks wie asyncio, twisted, aiohttp. Sie können auch miteinander verkettet werden, um Pipelines zu bauen und Probleme zu lösen.