
本文为AI帮者写的。在 Python 开发中“能跑就行”和“工程化”之间往往只隔着一个合理的目录结构。很多开发者在项目初期随意摆放文件导致后期出现循环导入、打包困难、路径混乱等问题。本文将从零开始带你打造一个专业级的 Python 工程涵盖目录结构、模块导入、开发模式安装以及 VSCode 的完美配置。1. 为什么推荐src/目录结构很多初学者习惯将代码直接放在项目根目录下扁平结构但对于中大型项目或需要打包发布的库src/结构是行业标准Django、Pandas、Flit 均采用此结构。它能有效隔离源代码与项目配置避免许多隐式错误。1.1 标准结构对比❌ 不推荐的扁平结构易出错my_project/ ├── my_package/ # 包 │ ├── __init__.py │ └── module.py ├── main.py # 入口脚本 └── setup.py风险运行python main.py时Python 会将当前目录my_project/加入sys.path。如果my_package和main.py互相导入极易引发循环导入或命名空间污染。此外tests/目录混在根目录下打包时可能被意外包含。✅ 推荐的 src 结构my_project/ ├── src/ # 源代码根目录 │ └── my_package/ # 实际的包 │ ├── __init__.py │ ├── module_a.py │ └── sub_package/ │ ├── __init__.py │ └── module_b.py ├── tests/ # 测试目录 ├── .gitignore ├── pyproject.toml # 现代打包配置替代 setup.py ├── README.md └── LICENSE1.2src/的核心优势避免意外导入代码在src/下运行时必须安装或指定路径才能导入防止了“因为刚好在同级目录就能 import”导致的隐式依赖。解决循环导入物理隔离了源代码和脚本强制使用包的方式引用减少循环依赖风险。打包更干净打包时只需指定src/为源目录不会把tests/、docs/等无关文件打进去。明确边界清晰区分“可安装的代码”和“项目配置/测试”。1.3src/带来的“麻烦”及解决方案src/结构确实增加了一点复杂度直接运行python src/my_package/main.py会报ModuleNotFoundError。解决方案开发模式推荐使用可编辑安装。# 在项目根目录执行pipinstall-e.这样 Python 环境会链接到src/之后你可以像普通包一样import my_package。运行模式使用-m参数。# 切换到项目根目录使用模块方式运行python-mmy_package.main2. 模块引用指南相对导入 vs 绝对导入在src/结构下理解导入语法至关重要。2.1 核心符号含义在from .文件名 import ...中.(单点)代表当前包当前目录。..(双点)代表父级包上一级目录。限制只能在包内的模块中使用即目录必须有__init__.pyPython 3.3 支持隐式命名空间包但建议显式创建__init__.py以明确包边界。禁忌不能在顶层脚本直接运行的.py文件中使用否则报错ImportError: attempted relative import with no known parent package。2.2 实战场景演示假设结构如下src/ └── my_package/ ├── __init__.py ├── module_a.py └── sub_package/ ├── __init__.py └── module_b.py场景 A同级模块引用需求在module_a.py中导入sub_package/module_b.py。# src/my_package/module_a.py# 错误 ❌: from module_b import x (会去系统路径找找不到)# 正确 ✅: 从当前包(my_package)进入 sub_packagefrom.sub_package.module_bimportsome_function场景 B下级模块引用父引用子同上也是相对导入的一种。场景 C上级/跨级引用子引用父需求在module_b.py中导入module_a.py。# src/my_package/sub_package/module_b.py# .. 表示返回上一级包 (my_package)from..module_aimportsome_function场景 D顶层脚本引用包绝对导入需求在项目根目录的main.py或外部脚本中引用。# main.py (位于项目根目录非 src 内)# 必须使用绝对导入frommy_package.module_aimportsome_functionfrommy_package.sub_package.module_bimportanother_function# 严禁使用: from .my_package import ... (会报错)2.3 相对导入 vs 绝对导入 对比表导入方式示例适用场景优点缺点相对导入from .module import xfrom ..sub import y包内部模块互引重构方便改包名不影响内部顶层脚本不可用路径深时可读性差绝对导入from my_package.module import x顶层脚本、跨包引用路径清晰全局可用包名重构需全局替换最佳实践建议包内部.py之间优先用相对导入from . import。包外部脚本引用包必须用绝对导入from my_package import。3. 开发神器pip install -e(可编辑模式)当你采用src/结构或开发一个库时pip install -e .是必备技能。3.1 它是做什么的-e是--editable的缩写。普通安装(pip install .)将代码复制到 Python 的site-packages目录。修改源码后需重新安装才生效。可编辑安装(pip install -e .)在site-packages中创建一个链接文件.egg-link或.pth指向你的本地源码路径。3.2 核心价值修改代码立即生效无需重装3.3 适用场景开发库/框架你在开发mylib同时有个test_app在引用它。在mylib目录下pip install -e .test_app就能直接用最新版mylib。本地项目联调多个微服务或模块在本地互相依赖用-e安装彼此。调试第三方库克隆开源库代码修改后用-e安装到环境中进行调试。3.4 注意事项必须有打包配置项目需包含setup.py或3.6PEP 518版本之后的pyproject.toml推荐。路径敏感如果移动了项目文件夹链接会失效需重新安装。建议用虚拟环境避免污染全局环境方便随时删除重试。卸载使用pip uninstall 包名即可移除链接。4. 标准工程及 VSCode 配置全攻略假设我们的项目结构如下my_awesome_project/ ├── .vscode/ # VSCode 配置目录建议加入 .gitignore │ ├── settings.json │ └── launch.json ├── src/ │ └── my_awesome_package/ │ ├── __init__.py │ ├── core.py │ ├── utils/ │ │ ├── __init__.py │ │ └── helpers.py │ └── main.py # 可选入口 ├── tests/ │ ├── __init__.py │ └── test_core.py ├── .gitignore ├── pyproject.toml # 现代打包配置 └── README.md代码内容src/my_awesome_package/utils/helpers.pydefhelper_func():returnI am a helpersrc/my_awesome_package/core.py(引用子模块)# 从同级的 utils 子包导入 helpersfrom.utils.helpersimporthelper_funcdefmain_logic():print(fCore logic calling:{helper_func()})src/my_awesome_package/main.py(包内入口引用同级)from.coreimportmain_logicif__name____main__:# 注意这里不能用相对导入因为这是直接运行的脚本# 但因为安装了包可以用绝对导入或者用 -m 运行main_logic()pyproject.toml(现代 Python 打包配置)[build-system] requires [setuptools61.0, wheel] build-backend setuptools.build_meta [project] name my_awesome_package version 0.1.0 description A fantastic package readme README.md requires-python 3.8 license {text MIT} [tool.setuptools.packages.find] where [src]如何运行与开发# 1. 创建虚拟环境python-mvenv venvsourcevenv/bin/activate# Windows: venv\Scripts\activate# 2. 可编辑安装 (关键步骤)pipinstall-e.# 3. 安装开发依赖如 pytestpipinstallpytest black# 4. 运行测试或脚本python-mmy_awesome_package.main pytest tests/4.1 选择解释器 (Select Interpreter)这是第一步也是最重要的一步。按CtrlShiftP(Mac:CmdShiftP)。输入Python: Select Interpreter。选择你项目虚拟环境如./venv/bin/python中的 Python 解释器。关键确保你已经在终端运行了pip install -e .这样 Python 环境才能识别my_awesome_package。4.2 配置智能提示与路径识别 (settings.json)如果不配置VSCode 的 Pylance 可能会在from my_awesome_package import ...下画红线提示Import my_awesome_package could not be resolved。解决方法在项目根目录创建.vscode/settings.json添加python.analysis.extraPaths。{python.analysis.extraPaths:[./src],python.testing.pytestArgs:[tests],python.testing.unittestEnabled:false,python.testing.pytestEnabled:true,editor.formatOnSave:true,editor.defaultFormatter:ms-python.black-formatter,[python]:{editor.codeActionsOnSave:{source.organizeImports:true}}}核心配置解释python.analysis.extraPaths: [./src]告诉 Pylance“请把./src目录当作源码根目录去扫描”。这样from my_awesome_package.core import ...就不会报错了。测试配置启用 pytest 并指定测试目录。格式化配置保存时自动用 Black 格式化并自动整理 import需安装 isort 插件或使用 Black 结合。4.3 配置调试与运行 (launch.json)在src/结构下直接按F5运行当前打开的文件如src/my_awesome_package/main.py通常会失败因为 Python 会把当前文件所在目录加入路径导致相对导入混乱。正确做法使用module模式模拟python -m命令。在.vscode/下创建launch.json{version:0.2.0,configurations:[{name:Python: 运行主程序 (Module模式),type:python,request:launch,module:my_awesome_package.main,console:integratedTerminal,justMyCode:true,cwd:${workspaceFolder}},{name:Python: 调试当前文件 (谨慎使用),type:python,request:launch,program:${file},console:integratedTerminal,env:{PYTHONPATH:${workspaceFolder}/src},justMyCode:true},{name:Python: 调试当前测试,type:python,request:launch,program:${file},purpose:[debug-test],console:integratedTerminal},{name:Python: 运行所有测试 (Pytest),type:python,request:launch,module:pytest,args:[-v,tests/],console:integratedTerminal}]}配置详解module: my_awesome_package.main这等同于在终端执行python -m my_awesome_package.main。VSCode 会自动处理sys.path确保能正确找到包。注意这里不需要写src.前缀因为包已经安装到了环境。cwd: ${workspaceFolder}将运行时的工作目录锁定在项目根目录。调试当前文件提供一个备用方案但需手动设置PYTHONPATH作为保险。不过包内文件仍可能因相对导入失败建议优先使用 Module 模式。测试调试直接利用 VSCode 的测试调试功能。4.4 集成测试流程配置好settings.json中的 pytest 参数后打开侧边栏的“测试”图标 (烧杯形状)。VSCode 会自动发现tests/下所有test_*.py文件。点击文件名旁的“运行测试”或“调试测试”按钮即可。如果测试代码中需要导入源码# tests/test_core.pyfrommy_awesome_package.coreimportmain_logicdeftest_main_logic():assertmain_logic()isnotNone# 假设 main_logic 返回 None此处仅为示例5. 完整开发工作流 (Cheat Sheet)5.1 初始化项目mkdirmy_awesome_projectcdmy_awesome_projectmkdir-psrc/my_awesome_package tests .vscodetouchsrc/my_awesome_package/__init__.pytouchtests/__init__.py# 创建其他文件...5.2 设置虚拟环境与依赖python-mvenv venvsourcevenv/bin/activate# Windows: venv\Scripts\activatepipinstall--upgradepip pipinstall-e.# 可编辑安装你的包pipinstallpytest black isort# 安装开发工具pip freezerequirements-dev.txt# 可选保存开发依赖5.3 VSCode 配置创建.vscode/settings.json(配置 extraPaths、格式化等)。创建.vscode/launch.json(配置 module 运行模式)。安装推荐插件Python (by Microsoft), Pylance, Black Formatter.5.4 编写代码与调试写代码在src/my_awesome_package/下编写利用 Pylance 的自动补全。运行按F5选择 “Python: 运行主程序 (Module模式)”。调试在代码行号左侧打红点按F5启动调试。测试在tests/下写测试用例利用侧边栏 Test 图标运行。6. 常见 VSCode 问题排查现象原因解决方案导入报红波浪线Pylance 没找到src/检查.vscode/settings.json的extraPaths是否为[./src]调试时 ModuleNotFound运行时路径不对不要用program: ${file}运行包内文件改用module: package.module找不到 pytest解释器没选对CtrlShiftP-Python: Select Interpreter选venv里的 Python相对导入报错直接运行了包内文件不要右键点击src/下的文件选 “Run Python File”要用F5配合 launch.json 的 module 模式Pylance 报错但代码能运行缺少 extraPaths添加extraPaths即可消除红线但代码本身能运行说明路径已通过安装解决保存时没有自动格式化未设置默认格式化器安装 Black 插件并在 settings.json 中设置editor.defaultFormatter: ms-python.black-formatter7. 总结目录结构中大型项目首选src/结构小型脚本可用扁平结构但建议尽早养成好习惯。导入规则包内部用相对导入(.和..)顶层脚本用绝对导入。开发流程养成pip install -e .的习惯配合虚拟环境开发体验极佳。打包意识即使不发布到 PyPI也要写好pyproject.toml这是现代 Python 工程化的基石。VSCode 配置settings.json-extraPaths解决智能提示。launch.json-module模式解决运行/调试。测试集成利用 VSCode 的测试面板一键运行 pytest事半功倍。配置好这些后你的 Python 工程将拥有专业级的开发体验代码提示精准、调试顺畅、测试自动化。现在就动手重构你的项目吧