当前位置: 首页 > news >正文

async 和 await

现代版本的 Python 有一种非常直观的方式来定义异步代码。这使它看起来就像正常的"顺序"代码,并在适当的时候"等待"。

当有一个操作需要等待才能给出结果,且支持这个新的 Python 特性时,你可以编写如下代码:

burgers = await get_burgers(2)

这里的关键是await。它告诉 Python 它必须等待get_burgers(2)完成它的工作 ,然后将结果存储在burgers中。这样,Python 就会知道此时它可以去做其他事情 (比如接收另一个请求)。

要使await工作,它必须位于支持这种异步机制的函数内。因此,只需使用async def声明它:

async def get_burgers(number: int): # Do some asynchronous stuff to create the burgers return burgers

...而不是def:

# This is not asynchronous def get_sequential_burgers(number: int): # Do some sequential stuff to create the burgers return burgers

使用async def,Python 就知道在该函数中,它将遇上await,并且它可以"暂停" 执行该函数,直至执行其他操作 后回来。

当你想调用一个async def函数时,你必须"等待"它。因此,这不会起作用:

# This won't work, because get_burgers was defined with: async def burgers = get_burgers(2)

因此,如果你使用的库告诉你可以使用await调用它,则需要使用async def创建路径操作函数 ,如:

@app.get('/burgers') async def read_burgers(): burgers = await get_burgers(2) return burgers

更多技术细节

你可能已经注意到,await只能在async def定义的函数内部使用。

但与此同时,必须"等待"通过async def定义的函数。因此,带async def的函数也只能在async def定义的函数内部调用。

那么,这关于先有鸡还是先有蛋的问题,如何调用第一个async函数?

如果你使用FastAPI,你不必担心这一点,因为"第一个"函数将是你的路径操作函数,FastAPI 将知道如何做正确的事情。

但如果你想在没有 FastAPI 的情况下使用async/await,则可以这样做。

编写自己的异步代码

Starlette (和FastAPI) 是基于 AnyIO 实现的,这使得它们可以兼容 Python 的标准库 asyncio 和 Trio。

特别是,你可以直接使用 AnyIO 来处理高级的并发用例,这些用例需要在自己的代码中使用更高级的模式。

即使你没有使用FastAPI,你也可以使用 AnyIO 编写自己的异步程序,使其拥有较高的兼容性并获得一些好处(例如, 结构化并发)。

我(指原作者 —— 译者注)基于 AnyIO 新建了一个库,作为一个轻量级的封装层,用来优化类型注解,同时提供了更好的自动补全内联错误提示等功能。这个库还附带了一个友好的入门指南和教程,能帮助你理解并编写自己的异步代码:Asyncer。如果你有结合使用异步代码和常规(阻塞/同步)代码的需求,这个库会特别有用。

其他形式的异步代码

这种使用asyncawait的风格在语言中相对较新。

但它使处理异步代码变得容易很多。

这种相同的语法(或几乎相同)最近也包含在现代版本的 JavaScript 中(在浏览器和 NodeJS 中)。

但在此之前,处理异步代码非常复杂和困难。

在以前版本的 Python,你可以使用多线程或者 Gevent。但代码的理解、调试和思考都要复杂许多。

在以前版本的 NodeJS / 浏览器 JavaScript 中,你会使用"回调",因此也可能导致“回调地狱”。

协程

协程只是async def函数返回的一个非常奇特的东西的称呼。Python 知道它有点像一个函数,它可以启动,也会在某个时刻结束,而且它可能会在内部暂停 ⏸ ,只要内部有一个await

通过使用asyncawait的异步代码的所有功能大多数被概括为"协程"。它可以与 Go 的主要关键特性 "Goroutines" 相媲美。

结论

让我们再来回顾下上文所说的:

Python 的现代版本可以通过使用asyncawait语法创建协程,并用于支持异步代码

现在应该能明白其含义了。

所有这些使得 FastAPI(通过 Starlette)如此强大,也是它拥有如此令人印象深刻的性能的原因。

路径操作函数

当你使用def而不是async def来声明一个路径操作函数时,它运行在外部的线程池中并等待其结果,而不是直接调用(因为它会阻塞服务器)。

如果你使用过另一个不以上述方式工作的异步框架,并且你习惯于用普通的def定义普通的仅计算路径操作函数,以获得微小的性能增益(大约100纳秒),请注意,在 FastAPI 中,效果将完全相反。在这些情况下,最好使用async def,除非路径操作函数内使用执行阻塞 I/O 的代码。

在这两种情况下,与你之前的框架相比,FastAPI可能仍然很快。

依赖

这同样适用于依赖。如果一个依赖是标准的def函数而不是async def,它将被运行在外部线程池中。

子依赖

你可以拥有多个相互依赖的依赖以及子依赖 (作为函数的参数),它们中的一些可能是通过async def声明,也可能是通过def声明。它们仍然可以正常工作,这些通过def声明的函数将会在外部线程中调用(来自线程池),而不是"被等待"。

其他函数

你可直接调用通过defasync def创建的任何其他函数,FastAPI 不会影响你调用它们的方式。

这与 FastAPI 为你调用路径操作函数和依赖项的逻辑相反。

如果你的函数是通过def声明的,它将被直接调用(在代码中编写的地方),而不会在线程池中,如果这个函数通过async def声明,当在代码中调用时,你就应该使用await等待函数的结果。

http://www.zskr.cn/news/112185.html

相关文章:

  • Applite:重塑Mac软件管理新体验的智能工具
  • 超实用的“财务数据统计报表-可视化图表”Excel模板分享!
  • 基于STM32和FreeRTOS的智能家居设计之路
  • ThinkPad终极静音方案:TPFanCtrl2双风扇智能控制系统深度解析
  • 5分钟搞定Android投屏:QtScrcpy零门槛操作指南
  • 拿捏 React 组件通讯:从父子到跨组件的「传功秘籍」
  • tensorflow 零基础吃透:tf.sparse.SparseTensor 与核心 TensorFlow API 的协同使用
  • 入职宇树Web前端开发,30K双休有点爽
  • tensorflow 零基础吃透:TensorFlow 张量切片与数据插入(附目标检测 / NLP 实战场景)
  • BetterNCM终极个性化定制:从零打造专属网易云音乐深度改造方案
  • Qwen3-32B实测:单卡A100跑出180+吞吐
  • 显卡驱动彻底清理终极指南:高效解决驱动冲突问题
  • ComfyUI-Manager完整教程:5步掌握AI绘画插件高效管理
  • 写论文软件挑到崩溃?听劝!别试了!虎贲等考 AI 已经把 “毕业级论文” 标准答案写好了
  • 探索ChinaAdminDivisonSHP:地理数据背后的数字艺术
  • Day29~删除一个字符串中出现次数最少的字符(小写字母)
  • 大麻叶病害缺陷检测数据集介绍-198张图片 智能农业监测 精准病害诊断 作物健康管理 农业科研分析 智能植保机器人 农业教育培训
  • 【OpenCV】Python图像处理几何变换之翻转
  • 口腔医学X光影像诊断检测数据集介绍-274张图片 临床辅助诊断 牙科影像自动分类 口腔疾病研究 远程医疗系统 医学教育培训
  • 百度网盘提取码智能获取完全解决方案
  • 探索低功耗多频带sub 1G射频芯片NRF905反向电路
  • 微信网页版限制突破神器:wechat-need-web插件终极解决方案
  • 终极NBTExplorer指南:3步学会我的世界数据编辑
  • AI赋能电力大数据挖掘!三大核心模型破解电网运维与负荷预测难题
  • 混乱市场的新罗盘:解读未来利率定价的全新模型
  • Video DownloadHelper CoApp 技术架构与部署指南
  • LobeChat能否对接Zoom?会议中实时AI字幕生成实验
  • 绝区零游戏自动化终极指南:10分钟从小白到高手
  • 汇川中型 PLC 纯 ST 语言双轴同步设备开发:初学者的友好指南
  • 明日提醒