Python 3新增关键字有哪些:async与await详解

夏知夏
夏知夏
发布时间:2026-11-01| 6人浏览过
原创

系统梳理Python 3相比Python 2新增与调整的保留关键字,重点深入详解async与await两个现代异步核心关键字的语法机制、底层执行逻辑、完整代码实战及高频避坑指南。

Python 从 2 升级到 3 经历了一次重大的语言底层重构与清理。在保留关键字层面,Python 3 正式引入了解决嵌套作用域修改的 nonlocal,以及在 Python 3.5 引入并在 Python 3.7 正式定型的 async 与 await;同时将 True、False、None 锁定为保留关键字,并将 print 与 exec 移出关键字列表转为内置函数。其中,async 与 await 是整个现代 Python 异步编程(Async I/O)最重要的语法支柱。本文首先完整梳理 Python 3 相比 Python 2 的关键字演变清单,随后深入拆解 async 与 await 的产生背景、核心语法机制、规范步骤与高频避坑规则。

一、Python 3 关键字演进全貌与新增清单

保留关键字(Keywords)是 Python 语言解析器在语法层面上预留的特殊词汇,不能用作普通变量名、函数名或类名。在标准库中,可以通过 keyword.kwlist 直接查询当前环境的所有保留关键字。

从 Python 2.7 的 31 个关键字过渡到 Python 3.7+ 的 35 个关键字,变化具体体现在以下几个维度:

变化类型 关键字名称 对应版本 主要作用与核心变更原因
新增保留关键字 nonlocal Python 3.0 允许在内层闭包函数中直接修改外层嵌套作用域(非全局)的局部变量。
新增保留关键字 async Python 3.5 / 3.7 声明原生协程函数(Native Coroutine)及异步上下文/迭代器语法。
新增保留关键字 await Python 3.5 / 3.7 挂起当前协程并将 CPU 控制权还给事件循环,等待可等待对象完成并取回值。
常量转升为关键字 True, False, None Python 3.0 Python 2 中这三者属于内置名称且允许被重新赋值(如 True = 0),Python 3 锁定为语法级保留字,彻底杜绝逻辑歧义。
移除的旧关键字 print, exec Python 3.0 由语句级关键字移出,重构为内置函数(如 print()),使传参(如 sep、end、file)更具扩展性。
软关键字(Soft Keywords) match, case, _ Python 3.10+ 用于结构化模式匹配(Pattern Matching)。仅在匹配语句上下文中作为关键字,不影响普通变量命名。
Python 3新增关键字有哪些:async与await详解
图 1:Python 关键字历史演变与新增分类全景示意

需要特别注意 async 与 await 的引入节奏:Python 3.5 首次通过 PEP 492 引入它们时,为了兼容既有代码库中可能已有的变量名,暂时将其作为“临时/软关键字(Provisional Keywords)”;从 Python 3.7 开始,它们被正式收录进 keyword.kwlist 成为不可重名的严格保留字。若在 Python 3.7 及以上版本中尝试书写 async = 1,解释器将直接抛出 SyntaxError: invalid syntax。

二、为什么引入 async 与 await:告别生成器伪协程

在 Python 3.4 之前,Python 原生并没有专门的协程语法。当时实现异步 I/O 必须依赖标准生成器机制:使用 @asyncio.coroutine 装饰器,并通过 yield from 语法进行任务交接与委托。

# Python 3.4 时代的旧式伪协程代码(已在 Python 3.8/3.10 被彻底废弃)
import asyncio

@asyncio.coroutine
def old_fetch_data():
    yield from asyncio.sleep(1)
    return "ok"

这种设计存在三个显而易见的设计缺陷:

  1. 概念边界混淆:普通数据生成器(Generator)与并发异步协程(Coroutine)在代码外观上完全一致,阅读代码时无法一眼区分究竟是在产出序列还是在执行异步调度。
  2. 语法生态受限:普通的 with 和 for 循环无法安全处理需要异步等待 I/O 的资源管理与流式迭代。
  3. 调试排查困难:若开发者遗漏了装饰器或误用了 yield,Python 解释器无法在编译期捕获错误,往往在运行期才会暴露难以追踪的逻辑断层。

为了从根本上解决这些问题,Python 3.5 发布了 PEP 492,正式确立了 原生协程(Native Coroutine) 的地位。通过专用的 async def 和 await 关键字,协程彻底与生成器解耦,获得了独立的数据类型和更加清晰严谨的语法约束。

Python 3新增关键字有哪些:async与await详解
图 2:同步顺序阻塞模型与 async/await 事件循环非阻塞调度模型对比

三、async 与 await 的核心语法与运行机制

1. async def:声明原生协程函数

在普通函数定义的 def 前加上 async 关键字,该函数就变成了协程函数(Coroutine Function)。它与普通函数最大的不同在于调用方式:

async def calculate():
    return 42

result = calculate()
print(type(result))
print(result)
$ python3 main.py
<class 'coroutine'>
<coroutine object calculate at 0x104b2c040>
sys:1: RuntimeWarning: coroutine 'calculate' was never awaited

如上述运行示例所示,直接调用协程函数不会执行函数体内的代码,而是立即返回一个处于未执行状态的“协程对象”。要想让它实际运行,必须将其放入事件循环中由调度器驱动,或者在另一个协程中通过 await 表达式等待其执行完毕。

2. await:挂起当前执行流并等待结果

await 只能在被 async def 修饰的函数内部使用。当代码执行到 await expr 时,当前协程会被立即挂起(Suspend),并将 CPU 控制权移交给底层的事件循环(Event Loop)。事件循环可以接着运行其他就绪任务;一旦被等待的操作完成,事件循环就会唤醒当前协程,恢复其执行上下文并获取返回值。

并不是所有对象都可以被 await。根据官方定义,只有实现了 __await__() 特殊方法的可等待对象(Awaitable)才能跟在 await 后面。主要包括以下三类:

  • 协程对象(Coroutine):调用 async def 函数得到的对象实例。
  • 任务(asyncio.Task):用于安排协程并发执行的高层包装对象,继承自 Future。
  • 期物(asyncio.Future):表示异步操作最终结果的低层对象,通常由框架底层使用。
重要语法约束

在普通同步函数(普通 def)中使用 await 会直接触发 SyntaxError: 'await' outside async function。同步与异步具有严格的传染性,不能随意混用。

四、标准操作步骤:从零构建与运行并发程序

在实际工程中,利用 async 与 await 编写并运行异步并发程序的标准流程分为以下四个关键步骤:

Python 3新增关键字有哪些:async与await详解
图 3:编写与运行 Python 异步程序的四个标准操作步骤

完整实战代码示例

下面的示例演示了同时并发模拟发起 3 个独立的 I/O 网络请求并聚合统计结果的完整流程:

import asyncio
import time

# 步骤一:使用 async def 定义协程任务,内部使用 await 挂起 I/O
async def fetch_api(source_id: int, delay_seconds: float) -> dict:
    print(f"[任务 {source_id}] 开始请求,预计耗时 {delay_seconds} 秒...")
    # 模拟非阻塞网络 I/O 等待
    await asyncio.sleep(delay_seconds)
    print(f"[任务 {source_id}] 数据就绪并返回!")
    return {"source": source_id, "status": "200 OK", "duration": delay_seconds}

# 步骤二与步骤三:主协程组织 Task 并通过 gather 聚合等待
async def main():
    start_time = time.perf_counter()
    print(">>> 主调度协程启动...")

    # 包装并发任务(排入事件循环就绪队列)
    task1 = asyncio.create_task(fetch_api(1, 1.2))
    task2 = asyncio.create_task(fetch_api(2, 0.8))
    task3 = asyncio.create_task(fetch_api(3, 1.5))

    # 使用 await 挂起主协程,并发等待全部任务结束
    results = await asyncio.gather(task1, task2, task3)

    elapsed = time.perf_counter() - start_time
    print(f">>> 全部任务执行完成,共获取 {len(results)} 条数据,总耗时: {elapsed:.2f} 秒")
    for item in results:
        print(f"  - 结果条目: {item}")

# 步骤四:通过 asyncio.run() 启动顶层事件循环
if __name__ == "__main__":
    asyncio.run(main())

示例运行结果

$ python3 demo_async.py
>>> 主调度协程启动...
[任务 1] 开始请求,预计耗时 1.2 秒...
[任务 2] 开始请求,预计耗时 0.8 秒...
[任务 3] 开始请求,预计耗时 1.5 秒...
[任务 2] 数据就绪并返回!
[任务 1] 数据就绪并返回!
[任务 3] 数据就绪并返回!
>>> 全部任务执行完成,共获取 3 条数据,总耗时: 1.51 秒
  - 结果条目: {'source': 1, 'status': '200 OK', 'duration': 1.2}
  - 结果条目: {'source': 2, 'status': '200 OK', 'duration': 0.8}
  - 结果条目: {'source': 3, 'status': '200 OK', 'duration': 1.5}

结果验证分析:在同步执行模型下,三个任务需要依次阻塞运行,总耗时为 1.2 + 0.8 + 1.5 = 3.5 秒;而在使用 async 与 await 进行任务协作后,三个 I/O 等待在底层并行交替进行,最终耗时仅为 1.51 秒(取决于最慢的单个任务)。

五、语法生态扩展:async with 与 async for

随着 async 与 await 的确立,Python 进一步延伸出专门配合上下文管理与迭代协议的复合语法:

1. 异步上下文管理器(async with)

用于在打开网络会话、数据库连接池或获取异步锁(asyncio.Lock)时,确保进入与退出时都能执行非阻塞的初始化与资源回收逻辑。底层依赖特殊方法 __aenter__() 与 __aexit__():

# 典型场景:在 aiohttp 或异步数据库连接中使用 async with
async with aiohttp.ClientSession() as session:
    async with session.get("https://api.example.com/data") as response:
        content = await response.text()
        print(content)
# 退出代码块时自动执行 await session.close(),安全释放连接资源

2. 异步迭代器(async for)

当迭代过程本身需要等待网络通信、流式文件块或消息队列推送时,传统的 for 循环无法等待下一条数据到达。async for 要求对象实现 __aiter__() 与 __anext__() 特殊方法,每次迭代都会自动挂起等待数据就绪:

# 典型场景:流式消费异步消息队列
async def message_consumer(queue):
    async for message in queue:
        print(f"收到推送消息: {message}")
        if message.is_terminal():
            break

六、高频错误排查与避坑指南

初学者在上手 async 与 await 时,最容易在以下四个场景出现错误或性能倒退:

1. 误在协程中直接调用同步阻塞库

典型错误表现:在 async def 函数中调用了传统的 time.sleep(5) 或 requests.get()。因为这些函数并不支持协作挂起,它们会直接把唯一的 Python 单线程主事件循环彻底卡死,导致所有并发任务全部暂停等待。

修复方案:I/O 等待必须使用异步原生库(如 asyncio.sleep()、httpx、aiohttp);如果必须调用无法重写的同步阻塞代码,应使用 asyncio.to_thread() 将其委托给外部工作线程池执行。

2. 遗漏 await 导致逻辑静默失效

典型错误表现:写了 fetch_data() 却忘了写 await,代码没有报错但数据没有拿到,终端打印 RuntimeWarning: coroutine was never awaited。

修复方案:只要函数被声明为 async def,在协程函数内部调用时必须加上 await 接收返回值,或者明确使用 asyncio.create_task() 包装转为后台调度。

3. 在循环中顺序使用 await 导致并发丢失

典型错误表现:在 for 循环内部逐个 await task:

# 错误反例:失去了并发优势,依然是串行执行
for url in urls:
    result = await fetch(url)  # 每次都会等上一个请求完全结束才发下一个

修复方案:先通过列表推导式批量创建 Tasks,然后使用 await asyncio.gather(*tasks) 统一触发并发等待。

七、知识全景与核心总结

回顾 Python 3 关键字的发展历程:从 Python 3.0 引入 nonlocal 并锁定基础常量,到 Python 3.5 引入并在 Python 3.7 正式确立保留字 async 与 await,Python 拥有了原生、规范且与生成器彻底解耦的异步编程体系。

Python 3新增关键字有哪些:async与await详解
图 4:Python 3 异步核心知识体系、语法组件与开发要点思维导图

在日常开发中,牢记以下三条核心准则:

  • 看清身份:async def 产出的是协程对象,唯有经由 await 或任务封装提交给事件循环,代码才会真正运转。
  • 保持非阻塞:事件循环的核心在于单线程分时复用,在协程链路中绝对不能混入同步 I/O 阻塞调用。
  • 善用并发工具:通过 asyncio.create_task() 和 asyncio.gather() 实现真正的并行等待与结果收集,最大化发挥异步编程的高吞吐优势。

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。