Python におけるコルーチンは、共同マルチタスクを通じて効率的な同時実行を実現できる軽量の同時プログラミング手法です。 yield キーワードを使用して関数の実行を一時停止し、現在の実行ステータスを保存することは、コルーチンの特別な機能です。したがって、コルーチンは特殊な種類のジェネレーター関数と考えることができます。コルーチンが一時停止されている場合、send メソッドを使用して実行を再開でき、再開時に値が返されます。
Python 3.4 より前は、「ジェネレーター コルーチン」と呼ばれるコルーチンを実装するために、yield キーワードがよく使用されていました。 asyncio モジュールが Python 3.4 に導入された後、async/await キーワードを使用して、「ネイティブ コルーチン」と呼ばれるコルーチン関数を定義できるようになりました。
スレッドやプロセスと比較して、コルーチンには次の利点があります。
軽量: コルーチンのコンテキスト切り替えコストは非常に小さく、単一スレッド内で同時実行できます。多数のコルーチンを実行します。
低レイテンシー: コルーチンの実行中に、スレッドの切り替えやロックとロック解除のオーバーヘッドがないため、外部イベントへの応答が速くなります。
効率: コルーチン コードは通常、マルチスレッドおよびマルチプロセス コードよりも簡潔で読みやすく、メンテナンス コストが低くなります。
コルーチンの使用シナリオには、ネットワーク プログラミング、非同期 I/O、データ ストリーム処理、同時実行性の高いタスクなどが含まれます。
Python 3 では、ジェネレーター コルーチンは、ジェネレーター関数を使用して実装されたコルーチンを指します。ジェネレーター関数は、ジェネレーター オブジェクトを返す特別な関数です。関数の実行は、yield ステートメントによって一時停止でき、次回ジェネレーター オブジェクトの "next"() メソッドが呼び出されたときに続行されます。
次は、単純なジェネレーター コルーチンの例です。これには、ジェネレーター関数コルーチンと単純な非同期 I/O 操作が含まれています。
import asyncio def coroutine(): print('Coroutine started') while True: result = yield print('Coroutine received:', result) async def main(): print('Main started') c = coroutine() next(c) c.send('Hello') await asyncio.sleep(1) c.send('World') print('Main finished') asyncio.run(main())
結果出力:
[root@workhost k8s]# python3 test.py
メインが開始されました
コルーチンが開始されました
コルーチンを受信しました: Hello
コルーチンを受信しました: World
メインが終了しました
上記のコードの実行プロセスを見てみましょう。
main 関数の実行が開始され、Main が開始されたと出力されます。
ジェネレーター オブジェクト c を作成し、 next(c) を呼び出して、最初の yield ステートメントでその実行を一時停止します。
c.send('Hello') を使用してジェネレーター関数の実行を再開し、ジェネレーター関数の戻り値として 'Hello' を使用します。
1 秒待機している間、main 関数は実行を一時停止し、イベント ループが次のタスクを開始するのを待ちます。
1 秒待った後、c.send('World') を使用してジェネレーター関数の実行を続行し、ジェネレーター関数の戻り値として 'World' を使用します。
main 関数は実行を再開し、Main completed を出力します。
ジェネレーター関数コルーチンを使用して、このコードは単純なコルーチンを実装します。ジェネレーター関数は、yield ステートメントを使用して関数の実行を一時停止します。その後、値をジェネレーター関数に渡して、send メソッドを介して関数の実行を再開できます。このように、ジェネレーター関数を使用して非同期同時実行を実現できます。例に示すように、ジェネレーター関数を使用して、非同期 I/O 操作の結果を受け入れ、出力します。
Python 3 では、新しいコルーチン タイプとしてネイティブ コルーチンが導入されています。ネイティブ コルーチンは async/await キーワードを使用して定義され、ジェネレーター コルーチンとは異なり、yield ステートメントを使用する代わりに return ステートメントを使用して通常の関数と同様に値を返すことができます。
次は、単純なネイティブ コルーチンの例です。これには、async キーワードで変更されたコルーチン関数と単純な非同期 I/O 操作が含まれています:
import asyncio async def coroutine(): print('Coroutine started') await asyncio.sleep(1) print('Coroutine finished') async def main(): print('Main started') await coroutine() print('Main finished') asyncio.run(main())
結果出力:
[root@workhost k8s]# python3 test.py
メインが開始されました
コルーチンが開始されました
コルーチンが終了しました
メインが終了しました
監視を続ける実行プロセスは次のとおりです:
main 関数の実行が開始され、「Main starting」と表示されます。
コルーチン関数を呼び出し、コルーチン オブジェクトとして実行します。
コルーチン関数で、Coroutine が開始されたことを出力します。
コルーチン関数で、 await asyncio.sleep(1) を使用して関数の実行を一時停止し、1 秒間待ちます。
1 秒後、コルーチン関数の実行を再開し、「Coroutine completed」を出力します。
main 関数は実行を再開し、Main completed を出力します。
上記のコードでは、ネイティブ コルーチン関数 coroutine が async キーワードを使用して定義されており、その中で await キーワードを使用して関数の実行を一時停止し、非同期 I/ ○作業完了です。ネイティブ コルーチンを使用すると、同時非同期コードを作成できるため、コードの効率とパフォーマンスが向上します。
Python 3 中,原生协程和生成器协程是不同的协程实现方式,它们分别具有独特的特点和适用场景。下面,通过对比它们的区别和优缺点,才可以更好地理解它们之间的异同,以便选择适合自己的协程实现方式,从而更好地编写高效、可维护的异步程序。
1.区别:
定义方式不同:原生协程使用 async/await 关键字来定义,而生成器协程使用 yield 关键字来定义。
返回方式不同:原生协程使用 return 语句来返回结果,而生成器协程使用 yield 语句来返回结果。
调用方式不同:原生协程使用 await 关键字来调用,而生成器协程使用 yield from 或 yield 语句来调用。
原生协程与生成器协程的实现方式不同,前者使用 asyncio 库,后者则是 Python 语言内置的特性。
2.优缺点:
原生协程的优点:
代码简洁易懂:使用 async/await 关键字,可以编写出更简洁易懂的协程代码。
性能更高:原生协程不需要创建生成器对象,也不需要通过 yield 语句来控制函数的执行流程,因此能够更加高效地处理异步操作。
支持异步 I/O 和任务处理:原生协程可以支持异步 I/O 操作和并发任务处理,可以在处理异步操作时更加灵活。
原生协程的缺点:
兼容性差:原生协程是 Python 3.5 版本之后才引入的新特性,因此在旧版本的 Python 中无法使用。
异常处理不方便:原生协程在处理异常时比较麻烦,需要使用 try/except 语句来处理。
生成器协程的优点:
兼容性好:生成器协程是 Python 2 和 Python 3 都支持的特性。
可读性好:生成器协程使用 yield 关键字来实现,代码逻辑清晰易懂。
异常处理方便:生成器协程在处理异常时比较方便,可以使用 try/except 语句来处理。
生成器协程的缺点:
性能相对较低:生成器协程需要创建生成器对象,也需要通过 yield 语句来控制函数的执行流程,因此处理异步操作时性能相对较低。
功能有限:生成器协程不能像原生协程一样支持异步 I/O 操作和任务处理。
接下来,模拟一个场景,假设实现一个异步的批量处理任务的工具,使用原生协程来实现。
看下面代码:
import asyncio import random async def batch_process_task(tasks, batch_size=10): # 将任务列表划分为多个批次 for i in range(0, len(tasks), batch_size): batch = tasks[i:i+batch_size] # 使用原生协程来异步处理每个批次的任务 await asyncio.gather(*[process_task(task) for task in batch]) async def process_task(task): # 模拟任务处理过程 await asyncio.sleep(random.uniform(0.5, 2.0)) print("Task {} processed".format(task)) async def main(): # 构造任务列表 tasks = [i for i in range(1, 101)] # 并发处理批量任务 await batch_process_task(tasks, batch_size=10) if __name__ == '__main__': asyncio.run(main())
输出:
[root@workhost k8s]# python3 test.py
Task 9 processed
Task 10 processed
Task 1 processed
Task 8 processed
Task 6 processed
Task 4 processed
Task 3 processed
Task 2 processed
Task 5 processed
...
...
batch_process_task函数使用原生协程来处理每个批次的任务,而process_task函数则是处理每个任务的函数。在main函数中,任务列表会被构造,并使用batch_process_task函数来异步地处理批量任务。
以上がPythonコルーチンの実装方法にはどのようなものがあるのでしょうか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。