python 踩坑(一)-类型&路径问题
## 坑二:pyright 类型不兼容(async def + @abstractmethod + yield)
问题现象:
1此 "chat_stream" 方法以不兼容的方式覆写了 "LLMProvider" 类中的同名方法2返回类型不匹配:基类方法返回 "CoroutineType[Any, Any, AsyncGenerator[str, None]]"3覆写方法返回 "AsyncGenerator[str, None]"根因:pyright 的类型推断遇到 async def + @abstractmethod + yield 的组合时,认为基类返回的是 Coroutine(因为 async def),但子类因为 yield 实际返回 AsyncGenerator——两个类型不兼容。
更根本的原因:Python 里 async def 默认返回 Coroutine,但一旦函数体内出现 yield,它就变成了 AsyncGenerator。pyright 在基类声明(只有 ...、没有 yield)时无法推断出这个变化。
解法:
python
1# 基类:去掉 async,让返回值类型明确为 AsyncGenerator2@abstractmethod3def chat_stream(self, messages: list[dict]) -> AsyncGenerator[str, None]:4 ...5
6# 子类:保留 async def + yield,pyright 能自己推断出 AsyncGenerator7async def chat_stream(self, messages: list[dict]) -> AsyncGenerator[str, None]:8 stream = await client.chat.completions.create(...)9 async for chunk in stream:10 yield chunk.choices[0].delta.content举一反三:
这条规则是通用的,不只在抽象基类:
- Python 里
def+yield= Generator(同步生成器) async def+yield= AsyncGenerator(异步生成器)- 任何一个
async def函数,只要写了yield,它的返回类型就从Coroutine变成AsyncGenerator——pyright 在调用处能正确推导,但类型标注处如果不写清楚就会报不兼容
如果基类里一定要 async def(为了强制子类也 async),就跳过 pyright 的检查:
python
1@abstractmethod2async def chat_stream(...) -> AsyncGenerator[str, None]: # type: ignore[override]3 ...4 yield ""但更干净的还是直接去掉基类的 async。
坑三:uvicorn import 路径(ModuleNotFoundError: No module named 'llm')
问题现象:
bash
1uv run uvicorn src.api.chat:app --port 80002# → ModuleNotFoundError: No module named 'llm'但同样这个模块用 python -m src.llm.demo 就能跑通。
根因:Python 有两种不同方式的「找到模块」机制:
| 启动方式 | Python 从哪找模块 | llm 能被找到吗 |
|---|---|---|
python -m src.llm.demo | 从当前工作目录搜 | ✅ src/llm/ 在目录下 |
uvicorn src.api.chat:app | 通过模块名递归导入,src 是被 import 的目标 | ❌ llm 不在 src 包里 |
关键差异:python -m 把当前目录加入 sys.path,所以 src 被当做顶层命名空间包。而 uvicorn 用 importlib.import_module("src.api.chat") 导入后,src 的父目录在 sys.path 中,但 llm 相对 src 的位置不在了。
错误尝试:加了 sys.path.insert(0, ...) 和 PYTHONPATH 也不行——因为当 src 被当作命名空间包(没有 __init__.py)导入时,Python 建立的模块查找上下文与直接 sys.path 插入不同。
正确解法:from src.llm.router import RouterClient——从 src 开始完全限定路径,不使用 from llm 这种相对顶层引用。