You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python导入问题:无法同时跑应用与pytest测试

问题梳理

结合你贴的代码逻辑,当前项目的实际目录结构为:

项目根目录/
├─ src/                  # 源码包,内含空的__init__.py文件
│  ├─ ClassA.py
│  ├─ ClassB.py
│  └─ main.py
└─ tests/                # 测试目录
   └─ test_app.py

你遇到的导入冲突本质是两种运行场景下,Python解释器的模块搜索路径(sys.path)初始化逻辑不同,导致导入上下文不统一:

  • 直接在项目根目录跑python src/main.py时,解释器会优先把src/目录加入搜索路径,此时ClassA、ClassB都被识别为顶层独立模块,写from ClassA import A可以正常找到文件;
  • 执行pytest时,测试代码会把项目根目录加入搜索路径,然后通过from src.ClassB import B的方式把src识别为一个顶层包,此时解释器会认为ClassA是src包内部的子模块,不会去src/目录下找顶层的ClassA文件,因此抛出ModuleNotFoundError: No module named 'ClassA';
  • 改成相对导入from .ClassA import A后,因为pytest场景下有明确的包层级上下文,导入可以正常运行;但直接跑python src/main.py时,解释器不会给入口脚本绑定src包的层级身份,相对导入的.没有父级参照,自然触发导入报错。
可行解决方案

按照推荐优先级排序:

方案1:统一包级绝对导入,用模块模式启动入口(最符合Python开发规范)

这个方案可以完全消除导入上下文的差异,后续如果要打包发布项目也不需要调整代码:

  • 统一修改所有模块的导入语句,全部基于顶层包src写绝对导入:
    • ClassB.py 中的导入改为:from src.ClassA import A
    • main.py 中的导入改为:from src.ClassB import B
  • 启动应用时,不要直接跑脚本的文件路径,在项目根目录执行python -m src.main
  • pytest场景下,只要保证项目根目录在模块搜索路径中(你现在测试代码里手动加路径的逻辑已经可以满足),导入逻辑完全统一,不会出现找不到模块的问题。

注意:python -m src.main和直接跑python src/main.py的核心区别是,-m参数会把当前工作目录(项目根目录)加入搜索路径,而不是把脚本所在的src/目录加入搜索路径,从根本上统一了两种运行场景的导入基准。

方案2:保留直接运行脚本的习惯,入口文件自动注入项目根路径

如果不想改变直接跑python src/main.py的使用习惯,只要在入口文件最开头加入根路径注入逻辑,保证所有运行模式下解释器都能正确识别src顶层包:

  • 修改main.py的开头部分:
    import sys
    import os
    # 自动定位项目根目录(即src文件夹的上级目录),插入模块搜索路径
    PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    sys.path.insert(0, PROJECT_ROOT)
    
    from src.ClassB import B
    
    if __name__ == '__main__':
        b = B()
        b.hello()
    
  • 同样把ClassB.py的导入语句改为from src.ClassA import A
  • 额外优化:可以去掉test_app.py里手动写的sys.path.append(...)逻辑,在项目根目录新建一个空的pytest.ini文件,pytest会自动把项目根目录加入搜索路径,测试代码直接写from src.ClassB import B即可,不需要手动处理路径。

方案3:兼容式导入(权宜方案,不推荐长期使用)

如果确实没法统一导入规则,可以在需要跨模块导入的ClassB.py里做异常捕获,同时兼容两种运行场景的导入逻辑:

try:
    # 优先适配包运行模式(pytest场景)
    from .ClassA import A
except ImportError:
    # 适配直接跑脚本的模式(直接运行同目录文件)
    from ClassA import A

这个方法虽然能临时解决问题,但导入逻辑分散,当项目规模变大后很容易出现不可控的导入异常,只适合非常小型的临时项目使用。

额外代码优化提示

你当前ClassB.hello方法里的print(a.hello())存在逻辑冗余:因为A.hello()方法本身已经执行了print('hello')且没有返回值,外层的print()会额外打印一个None,实际运行会多一行无意义的输出,可以直接调用a.hello()即可。

内容的提问来源于stack exchange,提问作者SecretIndividual

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.23 07:45:02