Asynchrones Python: Was Sie wissen müssen 🐍🐍🐍
Takashi Yamamoto
Infrastructure Engineer · Leapcell

Der Entwicklungsprozess von Python-Koroutinen und eine tiefgehende Analyse alter und neuer Koroutinen
1. Die historische Entwicklung von Python-Koroutinen
Im Laufe der langen Entwicklung von Python hat die Implementierung von Koroutinen mehrere bedeutende Veränderungen erfahren. Das Verständnis dieser Veränderungen hilft uns, das Wesen der asynchronen Programmierung in Python besser zu erfassen.
1.1 Frühe Erkundung und Einführung grundlegender Funktionen
- Python 2.5: Diese Version führte die Methoden
.send()
,.throw()
und.close()
für Generatoren ein. Das Erscheinen dieser Methoden machte Generatoren zu mehr als nur einfachen Iteratoren; sie begannen, komplexere Interaktionsfähigkeiten zu besitzen, was eine gewisse Grundlage für die Entwicklung von Koroutinen legte. Zum Beispiel kann die.send()
-Methode Daten in den Generator senden, was die bisherige Einschränkung aufhebt, dass Generatoren nur unidirektional ausgeben konnten. - Python 3.3: Die Syntax
yield from
wurde eingeführt, was ein wichtiger Meilenstein war. Sie ermöglichte es Generatoren, Rückgabewerte zu empfangen, und erlaubte es, Koroutinen direkt mityield from
zu definieren. Diese Funktion vereinfachte das Schreiben von Koroutinen und verbesserte die Lesbarkeit und Wartbarkeit des Codes. Mityield from
kann beispielsweise eine komplexe Generatoroperation bequem an einen anderen Generator delegiert werden, wodurch Code-Wiederverwendung und logische Trennung erreicht werden.
1.2 Unterstützung der Standardbibliothek und Syntaxverfeinerung
- Python 3.4: Das Modul
asyncio
wurde hinzugefügt, was ein entscheidender Schritt für Python hin zur modernen asynchronen Programmierung war.asyncio
bietet ein ereignisloopbasiertes asynchrones Programmierframework, das es Entwicklern ermöglicht, hocheffizienten asynchronen Code bequemer zu schreiben. Es bietet eine leistungsstarke Infrastruktur für das Ausführen von Koroutinen, einschließlich Kernfunktionen wie Ereignisschleifen und Aufgabenverwaltung. - Python 3.5: Die Schlüsselwörter
async
undawait
wurden hinzugefügt, die eine direktere und klarere Unterstützung für die asynchrone Programmierung auf Syntaxebene bieten. Das Schlüsselwortasync
wird verwendet, um eine asynchrone Funktion zu definieren, die anzeigt, dass die Funktion eine Koroutine ist; das Schlüsselwortawait
wird verwendet, um die Ausführung einer Koroutine zu pausieren und auf den Abschluss einer asynchronen Operation zu warten. Dieser syntaktische Zucker lässt den Schreibstil von asynchronem Code dem von synchronem Code ähnlicher erscheinen, wodurch die Schwelle für asynchrone Programmierung erheblich gesenkt wird.
1.3 Reife- und Optimierungsphasen
- Python 3.7: Die Art und Weise, Koroutinen mit
async def + await
zu definieren, wurde formell etabliert. Diese Methode ist prägnanter und unkomplizierter und hat sich zur Standardmethode zum Definieren von Koroutinen in Python entwickelt. Sie stärkte die syntaktische Struktur der asynchronen Programmierung weiter und ermöglichte es Entwicklern, asynchronen Code natürlicher zu schreiben. - Python 3.10: Das Definieren von Koroutinen mit
yield from
wurde entfernt, was eine neue Phase in der Entwicklung von Python-Koroutinen markiert. Der Fokus lag stärker auf dem neuen Koroutinensystem, das aufasync
undawait
basiert, wodurch die durch unterschiedliche Implementierungsmethoden verursachte Verwirrung reduziert wurde.
1.4 Konzepte und Auswirkungen alter und neuer Koroutinen
Alte Koroutinen basieren auf Generatorsyntax wie yield
und yield from
. Neue Koroutinen hingegen basieren auf Schlüsselwörtern wie asyncio
, async
und await
. In der Geschichte der Koroutinenentwicklung gab es eine Überschneidungsperiode der beiden Implementierungsmethoden. Die generatorbasierte Syntax alter Koroutinen führte jedoch leicht zu Verwechslungen zwischen den Konzepten von Generatoren und Koroutinen, was Lernenden einige Schwierigkeiten bereitete. Daher ist ein tiefes Verständnis der Unterschiede zwischen den beiden Implementierungsmethoden von Koroutinen entscheidend für die Beherrschung der asynchronen Programmierung in Python.
2. Überblick über alte Koroutinen
2.1 Kernmechanismus: Die Magie des yield
-Schlüsselworts
Der Kern alter Koroutinen liegt im yield
-Schlüsselwort, das Funktionen mit leistungsstarken Fähigkeiten ausstattet, darunter das Pausieren und Fortsetzen der Codeausführung, das abwechselnde Ausführen zwischen Funktionen und das Übertragen von CPU-Ressourcen.
2.2 Analyse von Codebeispielen
import time def consume(): r = '' while True: n = yield r print(f'[consumer] Starting to consume {n}...') time.sleep(1) r = f'{n} consumed' def produce(c): next(c) n = 0 while n < 5: n = n + 1 print(f'[producer] Produced {n}...') r = c.send(n) print(f'[producer] Consumer return: {r}') c.close() if __name__ == '__main__': c = consume() produce(c)
In diesem Beispiel ist die Funktion consume
eine Consumer-Koroutine und die Funktion produce
eine Producer-Funktion. Die while True
-Schleife in der Funktion consume
ermöglicht es ihr, kontinuierlich zu laufen. Die Zeile n = yield r
ist entscheidend. Wenn die Ausführung diese Zeile erreicht, pausiert der Ausführungsfluss der Funktion consume
, gibt den Wert von r
an den Aufrufer (d. h. die Funktion produce
) zurück und speichert den aktuellen Zustand der Funktion.
Die Funktion produce
startet die consume
-Koroutine über next(c)
und tritt dann in ihre eigene while
-Schleife ein. In jeder Schleife erzeugt sie ein Datenelement (n
) und sendet die Daten über c.send(n)
an die consume
-Koroutine. c.send(n)
sendet nicht nur Daten an die consume
-Koroutine, sondern setzt auch die Ausführung der consume
-Koroutine fort, wodurch sie ab der Zeile n = yield r
fortfährt.
2.3 Ausführungsergebnisse und Analyse
Ausführungsergebnisse:
[producer] Produced 1...
[consumer] Starting to consume 1...
[producer] Consumer return: 1 consumed
[producer] Produced 2...
[consumer] Starting to consume 2...
[producer] Consumer return: 2 consumed
[producer] Produced 3...
[consumer] Starting to consume 3...
[producer] Consumer return: 3 consumed
[producer] Produced 4...
[consumer] Starting to consume 4...
[producer] Consumer return: 4 consumed
[producer] Produced 5...
[consumer] Starting to consume 5...
[producer] Consumer return: 5 consumed
Wenn der Consumer consume
zu n = yield r
ausführt, pausiert der Prozess und gibt die CPU an den Aufrufer produce
zurück. In diesem Beispiel arbeiten die while
-Schleifen in consume
und produce
zusammen, um eine einfache Event-Loop-Funktion zu simulieren. Und die Methoden yield
und send
implementieren das Pausieren und Fortsetzen von Aufgaben.
Zusammenfassend lässt sich die Implementierung alter Koroutinen wie folgt darstellen:
- Ereignisschleife: Implementiert durch manuelles Schreiben von
while
-Schleifencode. Obwohl diese Methode relativ einfach ist, vermittelt sie Entwicklern ein tieferes Verständnis des Prinzips der Ereignisschleife. - Code-Pausieren und -Fortsetzen: Erreicht mit Hilfe der Eigenschaften des
yield
-Generators.yield
kann nicht nur die Funktionsausführung pausieren, sondern auch den Funktionszustand speichern, wodurch die Funktion nach dem Fortsetzen an der Stelle fortfahren kann, an der sie pausiert wurde.
3. Überblick über neue Koroutinen
3.1 Ein leistungsstarkes System basierend auf der Ereignisschleife
Neue Koroutinen werden basierend auf Schlüsselwörtern wie asyncio
, async
und await
implementiert, wobei der Ereignisschleifenmechanismus im Mittelpunkt steht. Dieser Mechanismus bietet leistungsfähigere und effizientere asynchrone Programmierfunktionen, einschließlich Ereignisschleifen, Aufgabenverwaltung und Callback-Mechanismen.
3.2 Analyse der Funktionen von Schlüsselkomponenten
- asyncio: Bietet eine Ereignisschleife, die die Grundlage für das Ausführen neuer Koroutinen bildet. Die Ereignisschleife ist für die Verwaltung aller asynchronen Aufgaben, die Planung ihrer Ausführung und die Sicherstellung verantwortlich, dass jede Aufgabe zum richtigen Zeitpunkt ausgeführt wird.
- async: Wird verwendet, um eine Funktion als Koroutinenfunktion zu kennzeichnen. Wenn eine Funktion als
async def
definiert ist, wird sie zu einer Koroutine, und das Schlüsselwortawait
kann verwendet werden, um asynchrone Operationen zu behandeln. - await: Bietet die Möglichkeit, den Prozess auszusetzen. In einer Koroutinenfunktion wird bei der Ausführung von
await
die Ausführung der aktuellen Koroutine pausiert, auf den Abschluss der asynchronen Operation nachawait
gewartet und dann die Ausführung fortgesetzt.
3.3 Detaillierte Erklärung von Codebeispielen
import asyncio async def coro1(): print("start coro1") await asyncio.sleep(2) print("end coro1") async def coro2(): print("start coro2") await asyncio.sleep(1) print("end coro2") # Create an event loop loop = asyncio.get_event_loop() # Create tasks task1 = loop.create_task(coro1()) task2 = loop.create_task(coro2()) # Run coroutines loop.run_until_complete(asyncio.gather(task1, task2)) # Close the event loop loop.close()
In diesem Beispiel werden zwei Koroutinenfunktionen coro1
und coro2
definiert. Nach dem Drucken von "start coro1" pausiert die Funktion coro1
für 2 Sekunden über await asyncio.sleep(2)
. Hier setzt await
die Ausführung von coro1
aus und gibt die CPU an die Ereignisschleife zurück. Die Ereignisschleife plant innerhalb dieser 2 Sekunden andere ausführbare Aufgaben, wie z. B. coro2
. Nach dem Drucken von "start coro2" pausiert die Funktion coro2
für 1 Sekunde über await asyncio.sleep(1)
und druckt dann "end coro2". Wenn coro2
pausiert ist und keine anderen ausführbaren Aufgaben in der Ereignisschleife vorhanden sind, wartet sie, bis die Pausenzeit von coro2
abgelaufen ist, und setzt dann die Ausführung des restlichen Codes von coro2
fort. Wenn sowohl coro1
als auch coro2
ausgeführt werden, endet die Ereignisschleife.
3.4 Ausführungsergebnisse und Analyse
Ergebnisse:
start coro1
start coro2
end coro2
end coro1
Wenn coro1
zu await asyncio.sleep(2)
ausführt, wird der Prozess ausgesetzt und die CPU an die Ereignisschleife zurückgegeben, wobei auf die nächste Planung der Ereignisschleife gewartet wird. Zu diesem Zeitpunkt plant die Ereignisschleife, dass coro2
die Ausführung fortsetzt.
Zusammenfassend lässt sich die Implementierung neuer Koroutinen wie folgt darstellen:
- Ereignisschleife: Erreicht durch die von
asyncio
bereitgestellteloop
, die effizienter und flexibler ist und eine große Anzahl asynchroner Aufgaben verwalten kann. - Programmaussetzung: Erreicht durch das Schlüsselwort
await
, wodurch die asynchronen Operationen von Koroutinen intuitiver und leichter verständlich werden.
4. Vergleich alter und neuer Koroutinenimplementierungen
4.1 Unterschiede in den Implementierungsmechanismen
- yield: Es ist ein Schlüsselwort für Generatorfunktionen. Wenn eine Funktion eine
yield
-Anweisung enthält, gibt sie ein Generatorobjekt zurück. Das Generatorobjekt kann schrittweise iteriert werden, um die Werte in der Generatorfunktion abzurufen, indem die Methodenext()
aufgerufen oder einefor
-Schleife verwendet wird. Durchyield
kann eine Funktion in mehrere Codeblöcke unterteilt werden, und die Ausführung kann zwischen diesen Blöcken umgeschaltet werden, wodurch das Pausieren und Fortsetzen der Funktionsausführung erreicht wird. - asyncio: Es ist eine von Python bereitgestellte Standardbibliothek zum Schreiben von asynchronem Code. Es basiert auf dem Event-Loop-Muster und ermöglicht die Verarbeitung mehrerer gleichzeitiger Aufgaben in einem einzigen Thread.
asyncio
verwendet die Schlüsselwörterasync
undawait
, um Koroutinenfunktionen zu definieren. In einer Koroutinenfunktion wird das Schlüsselwortawait
verwendet, um die Ausführung der aktuellen Koroutine zu pausieren, auf den Abschluss einer asynchronen Operation zu warten und dann die Ausführung fortzusetzen.
4.2 Zusammenfassung der Unterschiede
- Alte Koroutinen: Erreichen Koroutinen hauptsächlich durch die Fähigkeit des
yield
-Schlüsselworts, die Ausführung zu pausieren und fortzusetzen. Der Vorteil ist, dass es für Entwickler, die mit Generatoren vertraut sind, leicht zu verstehen ist, basierend auf der Generatorsyntax; die Nachteile sind, dass es leicht mit dem Generatorkonzept verwechselt werden kann und die Art und Weise, die Ereignisschleife manuell zu schreiben, nicht flexibel und effizient genug ist. - Neue Koroutinen: Erreichen Koroutinen durch den Ereignisschleifenmechanismus in Kombination mit der Fähigkeit des
await
-Schlüsselworts, den Prozess auszusetzen. Die Vorteile sind, dass es leistungsfähigere und flexiblere asynchrone Programmierfunktionen bietet, die Codestruktur klarer ist und es die Anforderungen der modernen asynchronen Programmierung besser erfüllt; Der Nachteil ist, dass für Anfänger die Konzepte der Ereignisschleife und der asynchronen Programmierung relativ abstrakt sein können und einige Zeit benötigen, um sie zu verstehen und zu beherrschen.
5. Die Beziehung zwischen await
und yield
5.1 Ähnlichkeiten
- Pausieren und Fortsetzen des Kontrollflusses: Sowohl
await
als auchyield
haben die Fähigkeit, die Codeausführung an einem bestimmten Punkt zu pausieren und zu einem späteren Zeitpunkt fortzusetzen. Diese Eigenschaft spielt eine entscheidende Rolle bei der asynchronen Programmierung und der Generatorprogrammierung. - Koroutinenunterstützung: Beide stehen in engem Zusammenhang mit Koroutinen. Sie können verwendet werden, um Koroutinen zu definieren und zu verwalten, wodurch das Schreiben von asynchronem Code einfacher und lesbarer wird. Unabhängig davon, ob es sich um alte oder neue Koroutinen handelt, verlassen sie sich auf diese beiden Schlüsselwörter, um die Kernfunktionen von Koroutinen zu erreichen.
5.2 Unterschiede
- Syntaxunterschiede: Das Schlüsselwort
await
wurde in Python 3.5 eingeführt und wird speziell verwendet, um die Ausführung in einer asynchronen Funktion zu pausieren und auf den Abschluss einer asynchronen Operation zu warten. Das Schlüsselwortyield
ist für frühe Koroutinen und wird hauptsächlich in Generatorfunktionen verwendet, um Iteratoren zu erstellen und eine Lazy Evaluation zu implementieren. Frühe Koroutinen wurden durch die Fähigkeiten von Generatoren realisiert. - Semantik:
await
bedeutet, dass die aktuelle Koroutine auf den Abschluss einer asynchronen Operation warten und die Ausführung aussetzen muss, wodurch anderen Aufgaben die Möglichkeit gegeben wird, ausgeführt zu werden. Es betont das Warten auf das Ergebnis einer asynchronen Operation und ist ein Wartemechanismus in der asynchronen Programmierung.yield
übergibt die Kontrolle über die Ausführung an den Aufrufer und speichert gleichzeitig den Zustand der Funktion, sodass sie die Ausführung beim nächsten Durchlauf an der angehaltenen Position fortsetzen kann. Es konzentriert sich mehr auf die Steuerung und den Erhalt des Zustands der Funktionsausführung.await
unterbricht das Programm und lässt die Ereignisschleife neue Aufgaben planen;yield
unterbricht das Programm und wartet auf die nächste Anweisung vom Aufrufer.
- Kontext:
await
muss in einem asynchronen Kontext verwendet werden, z. B. in einer asynchronen Funktion oder in einemasync with
-Block. Währendyield
in einer normalen Funktion verwendet werden kann, sofern die Funktion als Generatorfunktion definiert ist, auch ohne den Kontext der Verwendung von Koroutinen. - Rückgabewerte:
yield
gibt ein Generatorobjekt zurück, und die Werte im Generator können schrittweise durch Aufrufen der Methodenext()
oder Verwenden einerfor
-Schleife iteriert werden. Das Schlüsselwortawait
gibt ein awaitable-Objekt zurück, das einFuture
,Task
,Coroutine
usw. sein kann, das das Ergebnis oder den Zustand einer asynchronen Operation darstellt.
5.3 Zusammenfassung
await
implementiert das Pausieren und Ausführen von Programmen nicht über yield
. Obwohl sie ähnliche Fähigkeiten haben, haben sie überhaupt keine Aufrufbeziehung und sind beides Python-Schlüsselwörter. await
eignet sich für asynchrone Programmierszenarien, wird verwendet, um auf den Abschluss asynchroner Operationen zu warten, und unterstützt eine flexiblere Verwaltung von Koroutinen. yield
wird hauptsächlich in Generatorfunktionen verwendet, um Iteratoren und eine Lazy Evaluation zu implementieren. Es gibt einige Unterschiede in ihren Anwendungsszenarien und ihrer Syntax, aber beide bieten die Möglichkeit, den Kontrollfluss zu pausieren und fortzusetzen.
Durch die Überprüfung des Entwicklungsprozesses von Python-Koroutinen, den Vergleich der Implementierungsmethoden alter und neuer Koroutinen und die tiefgehende Analyse der Beziehung zwischen await
und yield
haben wir ein umfassenderes und tieferes Verständnis der Prinzipien von Python-Koroutinen. Obwohl diese Inhalte etwas schwer zu verstehen sind, wird die Beherrschung dieser Inhalte eine solide Grundlage für unsere Erkundungen im Bereich der asynchronen Programmierung in Python legen.
Leapcell: Die beste serverlose Plattform für das Hosting von Python-Apps
Zum Schluss möchte ich Leapcell vorstellen, die am besten geeignete Plattform für die Bereitstellung von Python-Anwendungen.
1. Unterstützung mehrerer Sprachen
- Entwickeln Sie mit JavaScript, Python, Go oder Rust.
2. Stellen Sie unbegrenzt viele Projekte kostenlos bereit
- Zahlen Sie nur für die Nutzung – keine Anfragen, keine Gebühren.
3. Unschlagbare Kosteneffizienz
- Pay-as-you-go ohne Leerlaufgebühren.
- Beispiel: 25 US-Dollar unterstützen 6,94 Millionen Anfragen bei einer durchschnittlichen Antwortzeit von 60 ms.
4. Optimierte Entwicklererfahrung
- Intuitive Benutzeroberfläche für mühelose Einrichtung.
- Vollständig automatisierte CI/CD-Pipelines und GitOps-Integration.
- Echtzeitmetriken und -protokollierung für umsetzbare Erkenntnisse.
5. Mühelose Skalierbarkeit und hohe Leistung
- Automatische Skalierung zur einfachen Bewältigung hoher Parallelität.
- Kein Betriebsaufwand – konzentrieren Sie sich einfach auf den Aufbau.
Erfahren Sie mehr in der Dokumentation!
Leapcell Twitter: https://x.com/LeapcellHQ