Skip to content

12 Python 面试题 ​

1、FastAPI 和 Flask 有什么区别? ​

FastAPI 和 Flask 都是常用的、使用 Python 开发的 Web 框架,但它们使用的场景有所不同。

Flask 是一个更轻量级的 Web 框架,生态非常成熟,适合快速迭代开发小型项目,但是它主要支持同步 API,整体并发能力不如 FastAPI。

FastAPI 是一个基于 Starlette 和 Pydantic 构建的 Web 框架,它原生支持 ASGI 和异步编程。FastAPI 还天然支持 Swagger 文档,方便 API 联调,因此,在需要高并发、开箱即用的中大型项目选择 FastAPI 更加适合。

2、FastAPI 为什么适合 Agent 项目? ​

一个AI Agent应用通常有以下特点:

  • IO 密集:一次请求可能涉及多个如 LLM 调用、RAG 检索、数据库查询、外部工具调用等操作。
  • 结构化输入输出:在 Agent 应用的相关请求中,通常包含 message、session_id、权限数据等信息,需要结构化数据进行校验。
  • 流式输出:LLM 生成答案往往耗时较长,为了保证用户体验,需要通过流式输出来返回结果。
  • 依赖管理:需要对认证数据库、Redis、向量数据库等依赖进行依赖注入。
  • API 文档:Agent 应用需要对外提供 API 文档方便前端或者其他客户端调用。

因为 FastAPI 对这些需求支持比较好,所以 FastAPI 更适合 Agent 项目。

3、Python 异步编程如何实现? ​

在 Python 中,异步编程主要依赖 async、await 和 asyncio 事件循环。

在 Python 中,可以使用 async 来定义异步函数:

python
async def get_data(url: str):
    await asyncio.sleep(2)  # 模拟网络请求
    return {"data": f"{url} 请求执行完成"}

在调用异步函数时,代码不会立即执行,而是会返回一个协程对象。并且协程需要通过事件循环调度来执行。其中 await 关键字表示协程需要等待异步操作执行完成。在等待期间,事件循环可以调度其他协程。

python
async def main():
    result = await get_data("https://api.test.com/v1/a")
    print(result)

asyncio.run(main())

除此之外,多个异步方法可以并发执行:

python
import asyncio
import datetime


async def get_data(url: str):
    await asyncio.sleep(2)  # 模拟网络请求
    return {"data": f"{url} 请求执行完成"}


async def main():
    await asyncio.gather(
        get_data("https://api.test.com/v1/a"),
        get_data("https://api.test.com/v1/b"),
        get_data("https://api.test.com/v1/c")
    )

print(f"开始时间:{datetime.datetime.now()}")
asyncio.run(main())
print(f"结束时间:{datetime.datetime.now()}")

执行结果如下,三个异步方法调用 3 次,每次耗时 2s,但是使用异步编程就可以做到 2s 全部执行完成。

image-20260727160848240

在 FastAPI 中,请求路由可以直接定义为 async def。框架内部会在 ASGI 运行环境中处理事件循环、调度请求。

4、asyncio 原理是什么? ​

asyncio 的核心实现机制是事件循环(Event Loop)。事件循环本质上可以理解为一个任务调度器,它在内部维护了一组协程任务,当某个任务遇到 await 需要等待 IO 时,事件循环就将这个任务挂起,切换去执行其他任务,等 IO 结束后,再恢复原来的任务执行。

asyncio 需要协程在 await 处主动让出控制权,事件循环才能调度执行其他任务,因此在异步方法中,不要直接执行阻塞操作。比如,在协程中用 time.sleep(10) 会阻塞整个事件循环,会导致其他任务也无法继续处理。所以应该使用 asyncio.sleep,或者把阻塞任务放到线程池中执行。

5、协程和线程有什么区别? ​

协程和线程都可以实现异步任务,但两者的调度方式、资源消耗、使用场景有所不同。

  • 调度方式

线程由操作系统调度,线程的切换是抢占式的。多个线程可以分配到不同 CPU 核心执行,但 CPython 中受 GIL(Global Interpreter Lock,全局解释器锁)影响,同一时刻只能有一个线程执行 Python 字节码,因此在处理 CPU 密集型任务时,无法通过多线程达到理想的效果。

协程是由事件循环进行调度的,任务间切换是协作式的。协程只有在遇到 await 时,才主动让出控制权,继续执行其他协程。

  • 资源开销

线程需要操作系统分配栈空间,创建和切换线程的成本较高。而协程更轻量,创建和切换成本较低。IO 密集型任务较多的系统,可以使用大量的协程任务。

  • 适用场景

线程适合并发数量不高、使用同步库(如 requests)或不方便改造成异步的 IO 密集型等场景。由于 CPython 存在 GIL,所以线程不适合 CPU 密集型计算。

协程更适合网络请求、LLM 调用、访问数据库、访问 Redis、调用第三方 API 等 IO 密集任务。

  • 编程复杂度

多线程开发需要关注锁、竞争条件、死锁等问题,使用起来比较复杂,而协程在单线程事件循环中运行,通过 await 主动让出执行权,一个线程就可以管理成百上千个并发任务。

6、WebSocket 和 SSE 区别是什么? ​

WebSocket 和 SSE 都可以实现服务器实时推送数据,但两者的通信模型不同。

SSE(Server-Sent Events) 本身是基于 HTTP 协议实现的,SSE 是服务器到客户端的单向推送。客户端通过一个普通 HTTP 请求建立连接,服务器可以持续返回数据,因此,非常适合 LLM 流式输出、日志推送、任务进度等场景。它实现简单,但是客户端不能主动发送数据给服务器端。

WebSocket 则是在 HTTP 握手完成后,升级为 WebSocket 协议,并建立了一个全双工的通信通道。连接建立之后,客户端和服务器端都可以随时主动发送消息,适用于在线聊天、实时游戏等需要频繁双向通信的场景。

在 AI 应用中,绝大多数场景都是将 LLM 生成的结果持续地推送给客户端,所以通常使用 SSE;如果 Agent 应用运行过程中需要客户端实时发送数据、确认操作等双向交互,则更适合使用 WebSocket。