深入理解Python協程:async def、async for、await、yield詳解

前言

在現代編程中,異步編程已成為提高程序效率和性能的重要方式。

Python 作為一種流行的編程語言,自然也提供了強大的異步編程支持。

本文將詳細介紹 Python 中的協程,以及 async def、async for、await 和 yield 等關鍵字的使用。


協程簡介

協程是一種比傳統函數更高級的控制結構。

它們在一個過程中暫停,然后在另一個地方恢復執行

協程可以在程序的多個點之間切換,從而實現并發執行,而無需多線程或多進程的開銷。


協程 vs 線程

與線程不同,協程由程序員手動控制其切換。

線程在操作系統級別進行調度,可能導致頻繁的上下文切換開銷。

協程則由 Python 解釋器調度,開銷更低,且不會發生競爭資源的問題。


一、async def 和 await

Python 3.5及之后版本中,引入了 asyncawait 關鍵字,使得定義和調用協程變得更為簡潔和直觀。

async def

async def 用于定義一個協程函數。

與普通函數不同,協程函數在調用時不會立即執行,而是返回一個協程對象,直到被 await 調用時才會運行。

import asyncioasync def my_coroutine():print("Hello")await asyncio.sleep(1)print("World")# 調用協程函數
coroutine = my_coroutine()

await

await 用于暫停協程的執行,等待另一個協程完成,并獲取其結果。

await 后面必須跟隨一個可等待對象。

協程、Future對象或其他實現了 __await__方法的對象。

async def main():print("Start")await my_coroutine()print("End")# 運行主協程
# asyncio.run(main())
  • 在上面的示例中,await my_coroutine() 會暫停 main 的執行,直到 my_coroutine 運行結束。
  • asyncio.run 這個函數是 Python 3.7 之后才有的特性。
  • 可以讓 Python 的協程接口變得非常簡單,一個好的編程規范是,asyncio.run(main()) 作為主程序的入口函數,在程序運行周期內,只調用一次 asyncio.run

二、async for 和 async with

Python 3.6 引入了 async for 和 async with,使得異步迭代和上下文管理變得更加方便。

async for

async for 用于異步迭代可等待對象的異步迭代器。

它的工作方式類似于普通的 for 循環,但可以在異步環境中使用。

class AsyncIterator:def __init__(self):self.count = 0async def __aiter__(self):return selfasync def __anext__(self):if self.count < 5:self.count += 1return self.countelse:raise StopAsyncIterationasync def async_for_example():async for number in AsyncIterator():print(number)asyncio.run(async_for_example())

async with

async with 用于異步上下文管理器。

它的作用與 with 語句類似,但適用于異步環境,確保在異步操作前后執行特定的設置和清理操作。

class AsyncContextManager:async def __aenter__(self):print("Enter context")return selfasync def __aexit__(self, exc_type, exc, tb):print("Exit context")async def async_with_example():async with AsyncContextManager() as manager:print("Inside context")asyncio.run(async_with_example())

三、yield 和 yield from

yield 和 yield from 是生成器相關的關鍵字。

但它們也可以用于協程中,尤其是在生成器協程(Python 3.3之前的異步實現)中。

yield

yield用于定義生成器函數。

生成器函數在每次 yield 語句處暫停,并在下次調用 next() 方法時繼續執行。

def simple_generator():yield 1yield 2yield 3for value in simple_generator():print(value)

yield from

yield from 用于委派生成器,允許一個生成器將部分操作委托給另一個生成器。

def generator1():yield 1yield 2def generator2():yield from generator1()yield 3for value in generator2():print(value)

在異步編程中,yieldyield from 也可以用于異步生成器和異步迭代器。


四、create_task 和 gather

asyncio.create_task 和 asyncio.gather 是兩個重要的工具,用于并發運行多個協程。

asyncio.create_task

asyncio.create_task 用于將協程包裝成任務,使其能夠在事件循環中并發運行。

import asyncioasync def task1():await asyncio.sleep(1)print("Task 1 completed")async def task2():await asyncio.sleep(2)print("Task 2 completed")async def main():task1_task = asyncio.create_task(task1())task2_task = asyncio.create_task(task2())# 等待所有任務完成await task1_taskawait task2_taskasyncio.run(main())

在這個示例中,我們創建了兩個任務 task1task2
并通過 asyncio.create_task 將它們包裝成可并發運行的任務。
然后,我們使用 await 等待所有任務完成。

asyncio.gather

asyncio.gather 用于并行運行多個協程,并收集它們的結果。

它比 create_task 更加方便,尤其是當我們需要同時運行多個任務并獲取它們的結果時。

import asyncioasync def task1():await asyncio.sleep(1)print("Task 1 completed")return "Result 1"async def task2():await asyncio.sleep(2)print("Task 2 completed")return "Result 2"async def main():results = await asyncio.gather(task1(), task2())print(results)asyncio.run(main())

在這個示例中,asyncio.gather 并行運行 task1task2
并在所有任務完成后返回一個包含結果的列表,這樣我們可以更方便地管理和處理多個協程任務。

異常處理與取消任務

在實際應用中,協程可能會拋出異常,或者需要在執行過程中取消某些任務。

我們可以通過 asyncio.gatherreturn_exceptions 參數來收集異常,同時也可以使用 cancel 方法來取消任務。

import asyncioasync def worker_1():await asyncio.sleep(1)return 1async def worker_2():await asyncio.sleep(2)return 2 / 0async def worker_3():await asyncio.sleep(3)return 3async def main():task_1 = asyncio.create_task(worker_1())task_2 = asyncio.create_task(worker_2())task_3 = asyncio.create_task(worker_3())await asyncio.sleep(2)task_3.cancel()res = await asyncio.gather(task_1, task_2, task_3, return_exceptions=True)print(res)# 輸出: [1, ZeroDivisionError('division by zero'), CancelledError()]
asyncio.run(main())
  • 在這個示例中,我們創建了三個任務 worker_1、worker_2 和 worker_3
  • 其中 worker_2 會拋出一個除零異常,而 worker_3 會在執行過程中被取消。
  • 我們使用 asyncio.gather 來收集所有任務的結果,并通過 return_exceptions=True 參數捕獲所有異常。
  • 最終的輸出包含了正常完成任務的結果、拋出的異常以及取消任務的狀態。

通過 asyncio.create_taskasyncio.gather,我們可以有效地并行運行多個協程任務,極大地提高程序的并發性能。
這在處理大量I/O操作或需要同時執行多個獨立任務的場景中尤為重要。


五、并發度控制asyncio.Semaphore

asyncio.SemaphorePythonasyncio 模塊中的一個重要工具,用于控制并發任務的數量。

它在處理大量并發操作時尤為重要,尤其是在需要限制同時運行的任務數量以避免過載或超出限制的場景中。

asyncio.Semaphore 是一種異步互斥量,允許在同一時間內有固定數量的任務訪問某個資源。

它可以幫助你在異步編程中控制并發級別,防止系統過載或超出外部服務的限制。

工作原理

1. 初始化:

semaphore = asyncio.Semaphore(value)
  • value 表示信號量的初始值,也即允許同時運行的任務數量。
  • 默認值是 1,表示互斥量(類似于鎖)。

2. 獲取信號量:

async with semaphore:# 受控的代碼塊
  • async with semaphore 是異步上下文管理器,獲取信號量(即允許繼續執行的許可證)并進入受控的代碼塊。
  • 當代碼塊執行完畢時,信號量會自動釋放,使其他任務能夠繼續執行。

3. 釋放信號量:

  • async with 語句塊結束時,信號量會自動釋放。
  • 這確保了每個獲取信號量的操作都有一個匹配的釋放操作。
使用場景
  • 控制并發任務數量:

使用 asyncio.Semaphore 來限制同時進行的任務數量。例如,當處理大量網絡請求時,控制并發度可以防止超出 API 的速率限制或避免過載。

  • 避免資源爭用:

當多個任務訪問共享資源時,信號量可以確保資源訪問的有序性和一致性,避免資源爭用問題。

示例

以下是一個簡單的示例,展示了如何使用 asyncio.Semaphore 來限制同時運行的任務數量:

import asyncioasync def worker(semaphore, worker_id):async with semaphore:print(f"Worker {worker_id} is working")await asyncio.sleep(1)print(f"Worker {worker_id} has finished")async def main():semaphore = asyncio.Semaphore(3)  # Limit concurrency to 3tasks = [worker(semaphore, i) for i in range(10)]await asyncio.gather(*tasks)if __name__ == "__main__":asyncio.run(main())

在這個例子中,Semaphore(3) 限制了最多同時運行 3worker 任務。

當有更多任務時,它們必須等待直到有信號量可用。

因此上述代碼的運行日志為:

Worker 0 is working
Worker 1 is working
Worker 2 is working
Worker 0 has finished
Worker 1 has finished
Worker 2 has finished
Worker 3 is working
Worker 4 is working
Worker 5 is working
Worker 3 has finished
Worker 4 has finished
Worker 5 has finished
Worker 6 is working
Worker 7 is working
Worker 8 is working
Worker 6 has finished
Worker 7 has finished
Worker 8 has finished
Worker 9 is working
Worker 9 has finished

總結

asyncio.Semaphore 是控制異步操作并發度的一個強大工具,它能夠有效管理任務并發,避免超載和資源爭用。

理解和正確使用信號量可以幫助你在異步編程中實現更高效、更可靠的代碼。


六、協程與生成器的關系

協程與生成器有很多相似之處,都能夠在函數執行過程中暫停并恢復,但它們的設計目的和使用場景有所不同。

相似之處

暫停與恢復:兩者都可以在執行過程中暫停,并在之后恢復。

關鍵字:協程使用 await 暫停執行,生成器使用 yield 暫停執行。

不同之處

生成器:主要用于生成一系列值,常用于迭代。

協程:主要用于處理異步操作,管理并發任務。

生成器:使用 yield 關鍵字。

協程:使用 async def 定義,await 關鍵字用于暫停。

控制流:

生成器:由調用方(迭代器)控制。

協程:由事件循環控制。

結合使用

在某些情況下,可以結合使用生成器和協程。

例如,在異步生成器中使用 yield 生成值,并使用 await 等待異步操作完成。

async def async_generator():for i in range(5):await asyncio.sleep(1)yield iasync def main():async for value in async_generator():print(value)asyncio.run(main())

在這個示例中,我們定義了一個異步生成器函數 async_generator,它每秒生成一個值,并在主協程中異步迭代這些值。


七、實際應用場景

異步編程在實際中有廣泛的應用,尤其是在處理I/O密集型任務時,如網絡請求、文件操作等。

通過異步編程,可以在等待I/O操作時執行其他任務,從而提高程序的并發性能。

異步網絡請求

import aiohttpasync def fetch(session, url):async with session.get(url) as response:return await response.text()async def main():async with aiohttp.ClientSession() as session:html = await fetch(session, 'http://example.com')print(html)asyncio.run(main())

在這個示例中,我們使用 aiohttp 庫進行異步網絡請求,大大提高了效率。

異步文件操作

import aiofilesasync def read_file(filename):async with aiofiles.open(filename, 'r') as f:contents = await f.read()print(contents)asyncio.run(read_file('example.txt'))

通過 aiofiles 庫,我們可以實現異步的文件讀寫操作,提高文件I/O操作的性能。

異步大模型流式服務

大模型(如GPT-4)相關的應用中,流式服務是一種常見的需求。
通過異步編程,可以實現高效的流式數據處理,提高服務響應速度。

import asyncioasync def stream_handler(reader, writer):while True:data = await reader.read(100)if not data:breakprint(f"Received: {data.decode()}")response = f"Echo: {data.decode()}"writer.write(response.encode())await writer.drain()writer.close()await writer.wait_closed()async def main():server = await asyncio.start_server(stream_handler, '127.0.0.1', 8888)async with server:await server.serve_forever()asyncio.run(main())

在這個示例中,我們使用 asyncio 庫創建了一個簡單的流式服務。

客戶端發送的數據會被接收并立即返回給客戶端,實現了基本的流式處理功能。


八、總結

Python的協程和異步編程為開發高效的并發程序提供了強大的工具。

通過 async def、await、async for 和 async with 等關鍵字,我們可以編寫簡潔、易讀的異步代碼。

理解和熟練應用這些關鍵字,將大大提高你的編程效率和程序性能。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/84608.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/84608.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/84608.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

基于功能基團的3D分子生成擴散模型 - D3FG 評測

D3FG 是一個在口袋中基于功能團的3D分子生成擴散模型。與通常分子生成模型直接生成分子坐標和原子類型不同&#xff0c;D3FG 將分子分解為兩類組成部分&#xff1a;官能團和連接體&#xff0c;然后使用擴散生成模型學習這些組成部分的類型和幾何分布。 一、背景介紹 D3FG 來源…

寫一個shell腳本,把局域網內,把能ping通的IP和不能ping通的IP分類,并保存到兩個文本文件里

寫一個shell腳本&#xff0c;把局域網內&#xff0c;把能ping通的IP和不能ping通的IP分類&#xff0c;并保存到兩個文本文件里 腳本1 #!/bin/bash #定義變量 ip10.1.1 #循環去ping主機的IP for ((i1;i<10;i)) doping -c1 $ip.$i &>/dev/null[ $? -eq 0 ] &&am…

如何提升企微CRM系統數據的準確性?5大核心策略詳解

在數字化客戶管理時代&#xff0c;企微CRM管理系統已成為企業連接客戶的核心平臺。但據統計&#xff0c;73%的企業因數據質量問題導致客戶分析失真、營銷效果下降。本文將深入解析影響數據準確性的關鍵因素&#xff0c;并提供可落地的優化方案&#xff0c;幫助企業在企微CRM軟件…

Unity輕松實現麥克風錄音與播放

文章目錄 概要錄音&#xff0c;播放音頻注意事項參考 概要 之前有想寫一個音樂播放的器的音頻功能&#xff0c;一直沒做&#xff0c;最近突然想寫&#xff0c;就寫了 錄音&#xff0c;播放 在語言模型中&#xff0c;編碼器和解碼器都是由一個個的 Transformer 組件拼接在一起…

七牛云圖片上傳 前后端全過程

相關網址&#xff1a;七牛開發者中心 相關網站&#xff1a; 七牛開發者中心 上傳流程概述 后端生成上傳憑證&#xff1a;服務器端使用七牛云 SDK 生成上傳憑證&#xff08;uptoken&#xff09;前端獲取憑證&#xff1a;前端通過 API 向后端請求上傳憑證前端上傳圖片&#xff1…

2025年AI生成PPT平臺推薦榜單:五大智能工具革新演示創作體驗

在數字化辦公飛速發展的當下&#xff0c;AI生成PPT平臺已成為職場人士、教育工作者和創意人群提升效率的利器。這些平臺憑借先進的人工智能技術&#xff0c;打破傳統PPT制作的局限&#xff0c;為用戶帶來便捷、高效且充滿創意的制作體驗。經過多維度測評&#xff0c;2025年AI生…

PHP框架在內容管理系統開發中的優勢:效率、安全與擴展性!

在當今快節奏的Web開發環境中&#xff0c;內容管理系統&#xff08;CMS&#xff09;已成為企業和個人建立動態網站的核心工具。傳統的手工編碼開發方式在面對復雜業務邏輯、頻繁迭代和安全要求時往往力不從心。而PHP框架&#xff08;如Laravel、ThinkPHP、Symfony&#xff09;的…

云原生安全實踐:CI/CD流水線集成DAST工具

&#x1f525;「炎碼工坊」技術彈藥已裝填&#xff01; 點擊關注 → 解鎖工業級干貨【工具實測|項目避坑|源碼燃燒指南】 ——從零掌握DevSecOps中的動態安全測試 一、基礎概念 1. DevSecOps DevSecOps 是將安全性&#xff08;Security&#xff09;融入開發&#xff08;Dev&am…

【C語言】基礎知識框架補充

前文主要介紹了C語言從零開始學習的基本框架與基礎知識導覽&#xff0c;本文主要補充此前未提及的學習內容&#xff0c;給有意精進C語言者指明一條可供參考的學習路徑。 補充一&#xff1a;動態內存管理 核心函數&#xff08;需#include <stdlib.h>&#xff09;&#xf…

垃圾識別檢測與分類數據集(貓臉碼客第244期)

目標檢測與垃圾&#xff1a;技術革新與環境管理的交匯點 在當今社會&#xff0c;城市化進程不斷加速&#xff0c;人口持續增長&#xff0c;垃圾處理問題愈發凸顯其重要性。有效管理垃圾&#xff0c;不僅關乎環境衛生狀況&#xff0c;更直接影響到城市的可持續發展以及居民的生…

【調研報告】2025年與2030年AI及AI智能體 (Agent) 市場份額分析報告

2025年與2030年AI及AI智能體 (Agent) 市場份額分析報告 摘要 本報告旨在深入分析全球人工智能&#xff08;AI&#xff09;市場及其子領域AI智能體的未來發展軌跡&#xff0c;重點關注其在2025年和2030年的市場規模及其占全球GDP和整體AI市場的比例。分析表明&#xff0c;AI市…

臺式機電腦CPU天梯圖2025年6月份更新:CPU選購指南及推薦

組裝電腦選硬件的過程中,CPU的選擇無疑是最關鍵的,因為它是最核心的硬件,關乎著一臺電腦的性能好壞。對于小白來說,CPU天梯圖方便直接判斷兩款CPU性能高低,準確的說,是多核性能。下面給大家分享一下臺式機電腦CPU天梯圖2025年6月版,來看看吧。 桌面CPU性能排行榜2025 臺…

小白學Pinia狀態管理

目錄 1. 什么是 Pinia&#xff1f; 2. 為什么需要 Pinia&#xff1f; 3. Pinia 的三個核心概念 State&#xff08;狀態&#xff09;- 存儲數據 Getters&#xff08;計算屬性&#xff09;- 處理數據 Actions&#xff08;方法&#xff09;- 修改數據 4. 創建一個簡單的 St…

Tauri2學習筆記

教程地址&#xff1a;https://www.bilibili.com/video/BV1Ca411N7mF?spm_id_from333.788.player.switch&vd_source707ec8983cc32e6e065d5496a7f79ee6 官方指引&#xff1a;https://tauri.app/zh-cn/start/ 目前Tauri2的教程視頻不多&#xff0c;我按照Tauri1的教程來學習&…

SQL進階之旅 Day 26:分庫分表環境中的SQL策略

【SQL進階之旅 Day 26】分庫分表環境中的SQL策略 文章簡述 隨著業務規模的擴大&#xff0c;單一數據庫難以承載海量數據與高并發訪問。分庫分表成為解決這一問題的關鍵手段&#xff0c;但同時也帶來了 SQL 查詢復雜度的顯著提升。本文作為“SQL進階之旅”系列的第26天內容&…

linux之 內存管理(6)-arm64 內核虛擬地址空間變化

一、新內核變動 kernel變化的真快&#xff0c;之前我記得4.x的內核的內核空間的線性映射區位于內核空間的高地址處的128TB&#xff0c;且當前的博客和一些書籍也都還是這樣介紹。可翻了翻kernel的Documentation/arm64/memory.rst文檔&#xff0c;發現最新的kernel已將這128TB移…

循環神經網絡(RNN):從理論到翻譯

循環神經網絡&#xff08;RNN&#xff09;是一種專為處理序列數據設計的神經網絡&#xff0c;如時間序列、自然語言或語音。與傳統的全連接神經網絡不同&#xff0c;RNN具有"記憶"功能&#xff0c;通過循環傳遞信息&#xff0c;使其特別適合需要考慮上下文或順序的任…

window批處理文件(.bat),用來清理git的master分支

echo off chcp 65001 > nul setlocal enabledelayedexpansionecho 正在檢查Git倉庫... git rev-parse --is-inside-work-tree >nul 2>&1 if %errorlevel% neq 0 (echo 錯誤&#xff1a;當前目錄不是Git倉庫&#xff01;pauseexit /b 1 )echo 警告&#xff1a;這將…

C#中的CLR屬性、依賴屬性與附加屬性

CLR屬性的主要特征 封裝性&#xff1a; 隱藏字段的實現細節 提供對字段的受控訪問 訪問控制&#xff1a; 可單獨設置get/set訪問器的可見性 可創建只讀或只寫屬性 計算屬性&#xff1a; 可以在getter中執行計算邏輯 不需要直接對應一個字段 驗證邏輯&#xff1a; 可以…

【mysql】聯合索引和單列索引的區別

區別核心&#xff1a;聯合索引可加速多個字段組合查詢&#xff0c;單列索引只能加速一個字段。 &#x1f539;聯合索引&#xff08;復合索引&#xff09; INDEX(col1, col2, col3)適用范圍&#xff1a; WHERE col1 ... ? WHERE col1 ... AND col2 ... ? WHERE col1 ..…