Python async, await 的理解与使用

本文介绍了Python中的async, await关键字,通过一个YouTube视频教程作为学习材料,探讨了它们在async函数中的作用,包括async函数的定义、调用和异步调用的原理。强调了在async函数中调用其他async函数时必须使用await关键字,以及何时不能使用await的情况,并提到了asyncio库在调用async函数中的角色。" 90207269,8542739,理解神经网络BP反向传播算法:原理与详细推导,"['深度学习', '机器学习', '神经网络', '反向传播算法', '权重优化']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关于 Python 中 async, await 关键字的一些知识在去年的一篇 探索 Flask 对 asyncio 的支持 有讲到,一直没在实际上用过它们,所以基本上也就忘干净了。随着 Flask 2 加入了 async 的特性,以及 FastAPI 从一开始支持 async, 又觉得有必要重新温习一下 Python 中如何使用 async, await 关键字了。

注:由于 Flask 支持了 async, 号称 async 化 Flask 的 Quart 项目开始变得无足轻重了。

本文主要的学习材料是在 YouTube 上的一个视频 Fear and Awaiting in Async (Screencast) , 其中用 Python REPL 以 Live 的形式展示,对 async, await 关键字循序渐进的讲解。

如今不少语言都支持 async, await 关键字,如 C#, JavaScript, Kotlin, Rust 等,还有今天的主角 Python。而 Java 仍然很重视函数返回值的意义,未支持 async, 只能显式的返回 Future/CompletableFuture, 而且自己控制如何在线程池中执行。

这又来到了一个叫做纤程的概念,它隐藏在 Python 的 async, await 背后,Go 语言对纤程的支持就更简单了, go foo() 就是了。

async 关键字的函数

>>> def greeting(name):
... return 'Hello ' + name
...
>>> greeting('Blog')
Hello Blog

>>> def greeting ( name ) :

. . . return 'Hello ' + name

. . .

>>> greeting ( 'Blog' )

Hello Blog

调用后,函数立即执行

对于一个普通的没有 async 关键字的函数,调用它直接得到它的返回值,如果给它加上 async 关键字会怎么样呢?

>>> async def greeting(name):
...     print('called gretting function')
...     return 'Hello ' + name
...
>>> greeting('Blog')
<coroutine object greeting at 0x10dc8ece0>

>>> async def greeting ( name ) :

. . .      print ( 'called gretting function' )

. . .      return 'Hello ' + name

. . .

>>> greeting ( 'Blog' )

< coroutine object greeting at 0x10dc8ece0 >

得到的是一个 coroutine 对象,函数没有立即执行。它有点像是其他语言的 Promise 或 Future,下一步需要对兑现它,在 Python 中要去兑现一个 coroutine 可调用它的 send() 方法

>>> g = greeting('Blog')
>>> g.send(None)
called gretting function
Traceback (most recent call last):
  File "<input>", line 1, in <module>
    g.send(None)
StopIteration: Hello Blog

>>> g = greeting ( 'Blog' )

>>> g . send ( None )

called gretting function

Traceback ( most recent call last ) :

   File "<input>" , line 1 , in < module >

     g . send ( None )

StopIteration : Hello Blog

coroutine.send(None) 触发了它的执行,同时注意到函数抛出了一个异常 StopIteration(Exception), 该异常的 value 就是 async 函数的返回值,所以也就可以定义一个 run() 函数来执行 async 函数

>>> def run(coro):
...     try:
...         coro.send(None)
...     except StopIteration as e:
...         return e.value
...
...
>>> run(greeting('Blog'))
called gretting function
'Hello Blog'

>>> def run ( coro ) :

. . .      try :

. . .          coro . send ( None )

. . .      except StopIteration as e :

. . .          return e . value

. . .

. . .

>>> run ( greeting ( 'Blog' ) )

called gretting function

'Hello Blog'

这就要问,加没有 async 有什么区别呢?我们先从字节码来对比以下两个方法的不同

  1. def foo()
  2. async def foo()
>>> def foo():
...     pass
...
>>> dis.dis(foo)
  2           0 LOAD_CONST               0 (None)
              2 RETURN_VALUE
>>> async def foo():
...     pass
...
>>> dis.dis(foo)
              0 GEN_START                1

  2           2 LOAD_CONST               0 (None)
              4 RETURN_VALUE

>>> def foo ( ) :

. . .      pass

. . .

>>> dis . dis ( foo )

   2            0 LOAD _ CONST                0 ( None )

               2 RETURN_VALUE

>>> async def foo ( ) :

. . .      pass

. . .

>>> dis . dis ( foo )

               0 GEN _ START                  1

   2            2 LOAD _ CONST                0 ( None )

               4 RETURN_VALUE

唯一的不同是加了 async 关键字的函数第一条指令是 GEN_START ,开始一个 Generator。

await, async 函数调用另一个 async 函数

明白了 async 函数返回的是一个  coroutine 对象后,这就能指导我们如何去调一个 async 函数,特别是由一个 async 函数调用另一个 async 函数

如果一个普通函数调用 async 函数,显然只会得到一个 coroutine 对象

>>> async def greeting(name):
...     return 'Hello ' + name
...
>>> def main():
...     print(greeting('Blog'))
...
...
>>> main()
<coroutine object greeting at 0x10ce37df0>
<bpython-input-9>:2: RuntimeWarning: coroutine 'greeting' was never awaited
  print(greeting('Blog'))
RuntimeWarning: Enable tracemalloc to get the object allocation traceback

>>> async def greeting ( name ) :

. . .      return 'Hello ' + name

. . .

>>> def main ( ) :

. . .      print ( greeting ( 'Blog' ) )

. . .

. . .

>>> main ( )

< coroutine object greeting at 0x10ce37df0 >

< bpython - input - 9 > : 2 : RuntimeWarning : coroutine 'greeting' was never awaited

   print ( greeting ( 'Blog' ) )

RuntimeWarning : Enable tracemalloc to get the object allocation traceback

并且 Python 解释器会发出警告说 coroutine was never awaited, 因为如果没有 await 的话,对 greeting('Blog') 的调用永远得不到执行,也就失去的调用的必要性。

如果由一个 async 函数按传统方式来调用另一个 async 函数会怎么样呢?

>>> async def main():
...     print(greeting('Blog'))
...
...
>>> main()
<coroutine object main at 0x10ce37840>
>>>
>>> run(main())
<coroutine object greeting at 0x10cf58120>
<bpython-input-14>:2: RuntimeWarning: coroutine 'greeting' was never awaited
  print(greeting('Blog'))
RuntimeWarning: Enable tracemalloc to get the object allocation traceback

>>> async def main ( ) :

. . .      print ( greeting ( 'Blog' ) )

. . .

. . .

>>> main ( )

< coroutine object main at 0x10ce37840 >

>>>

>>> run ( main ( ) )

< coroutine object greeting at 0x10cf58120 >

< bpython - input - 14 > : 2 : RuntimeWarning : coroutine 'greeting' was never awaited

   print ( greeting ( 'Blog' ) )

RuntimeWarning : Enable tracemalloc to get the object allocation traceback

没什么意外, main() 返回一个 coroutine, 其他什么也没发生。如果试图去兑现 main() 调用,相当于是普通函数调用了一个  async 函数。

这时就引出 await 关键字了,当一个 async 函数中调用另一个 async 函数时,必须在调用前加上 await 关键字,除非你就想得到一个 coroutine  对象。

>>> async def main():
...     print(await greeting('Blog'))
...
...
>>> run(main())
Hello Blog

>>> async def main ( ) :

. . .      print ( await greeting ( 'Blog' ) )

. . .

. . .

>>> run ( main ( ) )

Hello Blog

总结起来就是:由 async 函数发起的对其他 async 函数的调用时,都必须加上 await 关键时。这里 greeting() 也是一个 async 函数,如果它又调用其他的 async 函数,需应用同样的规则,相当于一个 await 链。当对入口的 async 函数发起执行(兑现)时,将会发现链式反应。

什么时候不能用 await 呢?

在 Python REPL 中对 async 函数不能用 await

>>> await main()
  File "<bpython-input-33>", line 1
SyntaxError: 'await' outside function

>>> await main ( )

   File "<bpython-input-33>" , line 1

SyntaxError : 'await' outside function

普通函数对 async 函数的调用不能 await

>>> def foo():
...     await greeting('Blog')
  File "<bpython-input-35>", line 2
SyntaxError: 'await' outside async function

>>> def foo ( ) :

. . .      await greeting ( 'Blog' )

   File "<bpython-input-35>" , line 2

SyntaxError : 'await' outside async function

上面那两种是一样的情况,因 Python REPL 就是用一个普通函数作为入口的

对一个普通函数用 await 语法上不报错,但执行会有问题

>>> async def foo():
...     print(await len('abc'))
...
...
>>> run(foo())
Traceback (most recent call last):
  File "<input>", line 1, in <module>
    run(foo())
  File "<input>", line 3, in run
    coro.send(None)
  File "<input>", line 2, in foo
    print(await len('abc'))
TypeError: object int can't be used in 'await' expression

>>> async def foo ( ) :

. . .      print ( await len ( 'abc' ) )

. . .

. . .

>>> run ( foo ( ) )

Traceback ( most recent call last ) :

   File "<input>" , line 1 , in < module >

     run ( foo ( ) )

   File "<input>" , line 3 , in run

     coro . send ( None )

   File "<input>" , line 2 , in foo

     print ( await len ( 'abc' ) )

TypeError : object int can 't be used in ' await ' expression

能不用用 await

  1. 针对 coroutine 使用 await, 也就是调用 async 修饰的函数用 await
  2. 发起调用的函数必须为一个 async 函数,或 coroutine.send(), 或者 asyncio 的 event loop
  3. 随着 Python 版本的演进,更多的地方可以用 await,只要跟着感觉走,哪里不能用 await 错了次就知道了

 如果对Python有兴趣,想了解更多的Python以及AIoT知识,解决测试问题,以及入门指导,帮你解决学习Python中遇到的困惑,我们这里有技术高手。如果你正在找工作或者刚刚学校出来,又或者已经工作但是经常觉得难点很多,觉得自己Python方面学的不够精想要继续学习的,想转行怕学不会的, 都可以加入我们,可领取最新Python大厂面试资料和Python爬虫、人工智能、学习资料!VX【pydby01】暗号优快云

asyncio 调用 async 函数

<think>好的,我现在需要帮助用户了解如何在Python中正确使用asyncawait进行异步编程。根据用户提供的引用资料和问题,我需要整理出一个结构清晰的回答,涵盖基本概念、语法规则、示例代码以及常见误区。 首先,我应该从asyncawait的基本作用开始。根据引用[1],async用于定义异步函数,而await用于挂起协程的执行,等待异步操作完成。这需要明确说明两者的角色和如何配合使用。 接下来,语法规则部分需要分点列出,比如如何定义异步函数,await使用限制,以及如何运行异步程序。引用[3]提到,await后面必须跟可等待对象,这点很重要,需要强调,避免用户错误使用。 然后是示例代码。用户提供的引用中有几个例子,比如引用[2]的say_after函数和main函数,以及引用[4]的async with语句。我需要选择或整合这些例子,展示基本的异步执行流程和实际应用场景,比如网络请求或文件操作。同时,要解释代码的执行顺序,特别是事件循环如何处理多个await调用。 常见误区部分,我需要提醒用户不要混用async和普通函数,注意await的位置,以及理解异步编程的非阻塞特性。例如,在引用[2]的例子中,main函数中的两个await say_after是按顺序执行的,总耗时3秒,但如果使用asyncio.gather来并发执行,可以缩短时间。这个对比可能对用户有帮助,应该加入。 最后,生成相关问题,可能需要包括性能分析、优化方法、错误处理等,但根据用户提供的示例问题,可能需要调整问题范围,确保相关且具体。 在整理过程中,需要确保所有代码示例用正确的格式,数学公式或变量用$...$,独立公式用$$...$$,但根据用户的问题,可能数学公式较少,主要关注代码和解释。同时,引用标识要自然地添加在引用内容的末尾,如引用[^1]、[^2]等。 现在需要检查是否有遗漏的关键点,比如事件循环的作用,async with和async for的用法,这些在引用[4]中提到,可能需要简要说明。另外,确保回答符合用户要求的结构:基本概念、语法规则、示例解析、常见误区,以及相关问题。 最后,语言要保持口语化,但结构清晰,分点明确,帮助用户逐步理解异步编程的核心概念。</think>### Pythonasyncawait关键用法及示例解析 #### 一、基本概念 1. **async**:用于定义异步函数(协程)。普通函数前添加`async`关键字后,调用时不会立即执行,而是返回一个协程对象。 2. **await**:用于挂起当前协程,等待异步操作完成。它只能在`async`定义的函数内部使用,且其后必须跟随可等待对象(如协程、`asyncio.Task`等)[^3]。 #### 二、语法规则 1. **定义异步函数** ```python async def my_coroutine(): result = await some_async_task() ``` 2. **运行异步程序** 必须通过事件循环启动: ```python import asyncio asyncio.run(my_coroutine()) # Python 3.7+ ``` 3. **await的限制** - 不可在普通函数中使用`await` - 不可直接嵌套多个`await`实现并发(需配合`asyncio.gather`或`Task`) #### 三、核心示例解析 ##### 示例1:基础执行流程 ```python import asyncio async def say_after(delay, msg): await asyncio.sleep(delay) print(msg) async def main(): print(f"开始时间: {asyncio.get_running_loop().time()}") await say_after(1, 'Hello') # 挂起1秒 await say_after(2, 'World') # 再挂起2秒 print(f"结束时间: {asyncio.get_running_loop().time()}") asyncio.run(main()) ``` *输出结果:总耗时约3秒* **关键点**:`await`会顺序执行协程,若需并发应使用`asyncio.gather`。 ##### 示例2:异步网络请求模拟 ```python async def fetch_data(): # 假设为异步IO操作(如HTTP请求) await asyncio.sleep(0.5) return "数据内容" async def process_data(): data = await fetch_data() print(f"收到数据: {data}") ``` **注意**:实际开发中需搭配支持异步的库(如`aiohttp`)。 ##### 示例3:异步上下文管理器 ```python async def async_open(filename): # 模拟异步文件打开 await asyncio.sleep(0.1) return open(filename) async def read_file(): async with async_open('data.txt') as f: # 异步上下文管理器 content = await f.read() print(content) ``` *引用自异步文件操作场景[^4]* #### 四、常见误区 1. **混淆普通/异步函数调用** ```python async def task(): print("执行任务") task() # 错误!未通过await或事件循环调用 ``` 2. **错误使用await位置** ```python def sync_func(): await async_task() # 报错!非async函数内使用await ``` 3. **误解异步非阻塞** 多个独立`await`仍为顺序执行,真正的并发需创建任务: ```python async def concurrent_demo(): task1 = asyncio.create_task(say_after(1, 'A')) task2 = asyncio.create_task(say_after(1, 'B')) await task1 await task2 # A和B同时输出 ``` #### 五、性能提升建议 - 使用`asyncio.gather`批量执行协程: ```python await asyncio.gather(task1(), task2()) # 并发执行 ``` - 通过`asyncio.TaskGroup`(Python 3.11+)管理任务组 - 避免在协程中执行阻塞式代码(如`time.sleep`)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值