
上周帮同事看一个跑不起来的脚本报错信息是ModuleNotFoundError: No module named requests但他的电脑上明明装过。打开终端敲pip show requests有结果换到 VSCode 里按 F5 就报错。问题花了不到两分钟就定位了VSCode 选中的解释器是另一个 Pythonpip装到了系统那个上面去。这类事我在过去几年里遇到过太多次几乎每次都是同一批坑——解释器选错、虚拟环境没激活、launch.json的cwd写成了相对路径、断点变灰点。这篇就围绕VSCode 配置 Python 开发环境这件事把从装完 Python 到能稳定打断点调试这条链路上真正会卡人的地方捋一遍。不铺概念只讲我实际配过、修过、被坑过的部分解释器怎么选、扩展装哪几个、settings.json该改什么、launch.json哪些字段最容易写错、终端和测试面板怎么接上、出问题以后按什么顺序排查。手上有 Python 但没在 VSCode 里正经跑起来过的人或者已经能跑但调试体验一直别扭的人都能直接拿去改。基础命令我会给全不会默认你记得住-m venv后面跟什么。1. 先把解释器这件事定下来为什么我劝你别在系统 Python 上跑项目1.1 全局解释器带来的三个隐性成本很多人第一次配 Python 环境是把官网下载的安装包装完勾上 Add to PATH然后直接在 VSCode 里写代码。能跑但代价在后面慢慢显出来。第一个成本是依赖版本冲突。你今天给 A 项目装了django3.2明天接了个老项目要求django2.2两个版本装在同一个 site-packages 里只能二选一装 B 就得卸 A卸完 A 项目又跑不动了。到第三个项目的时候你会开始怀疑自己是不是记错了什么。第二个成本是环境不可复现。你本地能跑同事pip install -r requirements.txt之后跑不起来因为你的环境里躺着一堆半年前随手pip install但没写进依赖文件的包你根本想不起来装过什么。这种幽灵依赖排查起来极其恶心因为它不报错只是行为不一样。第三个成本是破坏系统工具链。macOS 和大部分 Linux 发行版自带的那个 Python系统本身有脚本在用它。你在上面pip install --upgrade一些基础库把系统自带的版本顶掉了后面某些系统命令会莫名其妙报错而且很难联想到是自己升级 Python 包导致的。我自己的做法很简单系统 Python 只用来跑系统的东西一个项目一个虚拟环境环境目录跟着项目走。这是后面所有配置的地基地基不对launch.json配得再花哨都是白搭。1.2 venv、conda 与 uv 的取舍我实际怎么选环境管理工具的选择网上吵得很凶但落到实际场景其实没那么纠结。我把三种常用方案的差异整理成了下面这张表你可以直接对号入座。方案触发方式适合场景我踩过的问题venvpython -m venv .venv纯 Python 项目、Web 后端、脚本工具装带 C 扩展的包偶尔要本地编译环境conda / minicondaconda create -n xxx python3.11数据科学、需要非 Python 依赖CUDA、MKL环境重、conda activate和 VSCode 终端偶尔不同步uvuv venvuv pip install依赖解析慢、CI 环境、想快相对新团队里有人不熟需要统一说明我的默认选择是venv理由很朴素它是标准库自带的不需要额外装任何东西换电脑换系统都不会因为工具本身出问题。具体命令# 在项目根目录执行环境目录就落在 ./.venv python -m venv .venv # 激活Windows PowerShell .\.venv\Scripts\Activate.ps1 # 激活Windows CMD .\.venv\Scripts\activate.bat # 激活macOS / Linux source .venv/bin/activate装依赖的时候我习惯统一走python -m pip而不是直接pippython -m pip install -r requirements.txt这个习惯是吃过亏之后养成的。你开了虚拟环境理论上pip指向的就是当前环境的那个但如果 PATH 顺序出问题或者你的 shell 里有个 aliaspip完全可能指到别处去。python -m pip绑死了用当前这个解释器去跑 pip 模块不会有歧义。同理python -m pytest、python -m http.server我都这么写。数据科学方向的朋友如果确实需要 CUDA 相关的二进制依赖conda 还是省事但要注意一点在 VSCode 里选解释器时一定要选到envs/环境名/bin/python这一层而不是 base 环境的那个。我见过有人选对了 conda 环境名但 VSCode 实际用的是 base报错信息里带的是 base 的路径看起来像选了没用。2. VSCode 侧的三件套扩展、解释器与大项目索引2.1 Python 扩展和 Pylance 各自管什么装扩展这件事我的原则是只装必要的那几个装完就不动了。核心就两个PythonMicrosoft 官方那个扩展 ID 是ms-python.python和Pylance。前者负责解释器选择、运行、调试、终端集成后者负责类型推断、自动补全、跳转定义。装Python的时候一般会提示你把Pylance一并装上接受就行。剩下两个看情况Black Formatter或者Ruff格式化。团队有统一规范就装没规范就先用 Ruff它同时管 lint 和 format配置一份就够。Jupyter只有在你要在 VSCode 里跑.ipynb或者用交互式窗口分块执行时才装。不跑 notebook 完全不需要装了会多出一堆命令和界面元素。有个细节值得说扩展是装在工作区还是全局会影响别人拉你的代码之后能不能直接跑。多人协作时我会在项目里放一个.vscode/extensions.json{ recommendations: [ ms-python.python, ms-python.vscode-pylance, charliermarsh.ruff ] }新人克隆仓库VSCode 会弹一个此工作区推荐安装扩展的提示点一下装齐。这比在群里发记得装 Python 扩展靠谱得多也比写进 README 里有效——README 没人看。2.2 让编辑器认对那个解释器解释器选择入口有三个用哪个都行命令面板CtrlShiftP/CmdShiftP输入Python: Select Interpreter底部状态栏左侧那个 Python 版本号点一下就能选打开任意.py文件后右下角状态栏。选的时候注意识别路径。列表里通常会出现这么几类带.venv、venv、env字样的是项目内的虚拟环境优先选这个路径里带conda、envs的是 conda 环境路径是/usr/bin/python3、C:\Users\你\AppData\Local\Programs\Python\Python311\python.exe这种的是系统解释器。选完之后建议做一次验证在 VSCode 内置终端里敲python -c import sys; print(sys.executable)输出的路径应该和状态栏显示的一致。这一步花十秒钟能省掉后面半小时的困惑。我上面提到的那个同事就是状态栏看着是.venv结果终端里的 shell 还是系统 Python两边不一致。2.3 settings.json 里我固定会改的几项工作区级的.vscode/settings.json是我每次新开项目都会先写好的文件。下面这份是我常用的基础版{ python.defaultInterpreterPath: ${workspaceFolder}/.venv/bin/python, python.terminal.activateEnvironment: true, python.analysis.typeCheckingMode: basic, python.analysis.autoImportCompletions: true, python.analysis.diagnosticSeverityOverrides: { reportMissingTypeStubs: none }, editor.formatOnSave: true, [python]: { editor.defaultFormatter: charliermarsh.ruff }, files.exclude: { **/__pycache__: true, **/.pytest_cache: true, **/*.egg-info: true } }python.defaultInterpreterPath里 Windows 用户要把路径改成.venv\\Scripts\\python.exe。注意这个字段只在工作区还没有记录过解释器选择时生效如果你之前手动选过VSCode 会把选择存在自己的状态里改这个字段不一定立刻生效——遇到这种情况重新执行一次Select Interpreter覆盖掉即可。python.analysis.typeCheckingMode我常年用basic。strict在存量项目上会瞬间刷出上千条波浪线看着就烦最后的结果是所有人把它关掉反而失去了类型检查的意义。新项目可以试strict老项目basic起步。reportMissingTypeStubs设成none是我个人偏好。很多第三方库不带.pyi类型存根basic模式下也会提示提示了也没法修不如关掉让波浪线只保留真正能动手解决的问题。3. launch.json 配置断点、参数、环境变量一次配齐3.1 生成 launch.json 的入口别点错launch.json我见过最多的问题是位置放错了。它必须在.vscode/launch.json和settings.json同级。有人把它建在项目根目录或者建在.vscode/.vscode/下面VSCode 完全读不到按 F5 走的还是默认行为。正确的入口是侧边栏的运行和调试面板CtrlShiftD点创建 launch.json 文件然后选Python Debugger再选一个模板。模板选完会生成一份带注释的配置把name、program改一改就能用。如果项目根目录不是当前打开的文件夹比如代码在src/下program的路径要用${workspaceFolder}拼{ version: 0.2.0, configurations: [ { name: Python: 当前文件, type: debugpy, request: launch, program: ${file}, console: integratedTerminal, cwd: ${workspaceFolder} } ] }注意type字段。老版本生成的是type: python新版本 Python 扩展已经切到type: debugpy。两者目前都还能用但如果你的调试器一直启动不起来先看看这个字段改成debugpy试一次。3.2 最容易配错的字段cwd、args、env这三个字段我几乎每次帮人看配置都会遇到写错的。cwd决定工作目录也就是os.getcwd()返回什么。默认是${workspaceFolder}如果你的脚本靠相对路径读配置文件open(config/app.yaml)而文件实际在src/config/下那就会报FileNotFoundError。解决方式有两种改cwd或者改代码用绝对路径。生产代码我更推荐后者但临时调试前者更快。args是命令行参数数组注意它是数组不是字符串args: [--input, data/raw.csv, --limit, 100]我见过写成--input data/raw.csv的程序收到的是一个带空格的完整字符串argparse解析出来只有一个参数然后报缺少必需参数。这种错误信息看着很迷惑因为你在终端里手动敲同样的命令是能跑的。env是环境变量调试时经常用来临时切配置env: { APP_ENV: dev, LOG_LEVEL: DEBUG }, envFile: ${workspaceFolder}/.envenv和envFile可以同时用env里的会覆盖envFile里的同名项。这里有个安全习惯值得养成.env加进.gitignore只提交.env.example。调试时把数据库密码写进launch.json然后提交上去这种事我见过不止一次清理起来非常麻烦。3.3 Web 框架与多进程脚本的配置差异不同项目的启动方式差别挺大我把几种常见场景的配置整理在下面。调试 Flask{ name: Flask, type: debugpy, request: launch, module: flask, env: { FLASK_APP: app.py, FLASK_DEBUG: 1 }, args: [run, --no-debugger, --no-reload], jinja: true }关键在于--no-reload和--no-debugger。Flask 自带的 reloader 会 fork 出子进程VSCode 的调试器挂到父进程上你打的断点在子进程里根本不生效。关掉 reloader 之后断点才正常。代价是改代码要手动重启但调试期间我不在乎这个。调试 Django{ name: Django, type: debugpy, request: launch, program: ${workspaceFolder}/manage.py, args: [runserver, --noreload], django: true }同理--noreload必须加。调试 pytest 的单个用例{ name: pytest 当前文件, type: debugpy, request: launch, module: pytest, args: [${file}, -v, -s], console: integratedTerminal, justMyCode: false }justMyCode设成false之后单步可以走进第三方库的源码。排查到底是我调错了还是库有 bug的时候非常有用代价是单步时会跳进一堆不关心的文件日常调试我还是保持true需要的时候临时改一下。多进程、多线程脚本要注意调试器默认只挂主进程。子进程里的断点不会命中。这种情况我一般改成在子进程入口加日志或者把并发度临时降到 1 把逻辑跑通再调回来。3.4 附加到已经跑起来的进程有些问题没法通过从 VSCode 启动复现比如服务已经由 supervisor 拉起来了、或者是别人启动的服务你只是去连一下。这时候用 attach 模式{ name: Python: 附加, type: debugpy, request: attach, connect: { host: localhost, port: 5678 }, pathMappings: [ { localRoot: ${workspaceFolder}, remoteRoot: /app } ] }目标进程需要用调试器启动才行python -m debugpy --listen 5678 --wait-for-client your_script.py--wait-for-client会让进程停在那里等调试器连上不加的话可能你还没 attach代码已经跑完退出了。pathMappings是远程调试时的关键本地路径和远程路径不一致时必须配否则断点位置会全部错位。4. 终端、测试与交互式窗口把运行入口收拢到一处4.1 终端为什么要自动激活虚拟环境python.terminal.activateEnvironment设为true之后你在 VSCode 里新开的终端会自动执行激活脚本。这个功能在 Windows 上偶尔会翻车报一段类似无法加载文件 Activate.ps1因为在此系统上禁止运行脚本的错误。原因在 PowerShell 的执行策略。默认策略是Restricted不允许执行.ps1脚本。解决办法是给当前用户放宽到RemoteSignedSet-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned只改当前用户不要动LocalMachine改全局会影响到这台机器上其他使用者的安全策略。改完之后把终端关掉重开。如果你用的是 CMD 或者 Git Bash 作为默认终端一般不会有这个问题。默认终端的配置在settings.json里terminal.integrated.defaultProfile.windows: PowerShell这里我个人的建议是统一团队用的终端类型。混着来会出现我这能跑你那不能跑的情况很多时候只是激活脚本的语法不同。4.2 pytest 在测试面板里的配置VSCode 的测试面板能直接把测试用例列出来点单个用例旁边的三角就能跑对调试很有帮助。要让它工作需要先启用测试框架在settings.json里写{ python.testing.pytestEnabled: true, python.testing.unittestEnabled: false, python.testing.pytestArgs: [tests] }两个Enabled不能同时为true否则会提示冲突。pytestArgs里指定测试目录如果你的测试不在tests/下改成实际目录。这里有个我踩过的坑项目根目录如果有多个 Python 包pytest 的导入路径可能不对。表现是终端里python -m pytest能跑但测试面板里报ImportError。原因是测试面板启动进程时的cwd和终端不一样。解决办法是在项目根目录加一个pyproject.toml或者pytest.ini明确 rootdir[tool.pytest.ini_options] testpaths [tests] pythonpath [.]pythonpath这一项是老版本 pytest 需要额外插件才支持的新版已经内置了。加上之后两边行为就一致了。4.3 交互式窗口适合干什么活按ShiftEnter会把当前行或选中的代码块送到 Python 交互式窗口执行上下文是连续的——前面定义的变量后面能直接用。这个功能我用得挺频繁但只用在特定场景调数据处理的中间结果读一个大 CSV加载那三行不想反复跑就在交互窗口里分块执行看 DataFrame 的形状、看几行样例试探性地调库的 API不确定某个参数什么意思直接在交互窗口里试比写个临时脚本快验证正则表达式边写边看匹配结果。它不适合的场景也很明确别用它跑最终结果。交互窗口的状态是累积的你改了上面的代码重新执行下面的变量还是旧的很容易得出错误结论。最后验证逻辑一定要从头完整跑一遍最好在干净的终端里跑。另外交互窗口占内存不小导入了 pandas、torch 这种大库之后一个窗口吃几百 MB 很正常不用了记得关掉。5. 排查链路从能跑但报红到断点失效5.1 Import 波浪线报红但程序能正常跑这个现象极其常见也极其让人分心。典型表现import requests下面画着黄波浪线鼠标悬停提示Import requests could not be resolved但终端里python main.py跑得好好的。根因通常是Pylance 用的解释器和实际运行用的解释器不是同一个。Pylance 是语言服务器它有自己的解释器解析逻辑运行时用的是你 F5 启动时选的那个。两者不一致就出现这个现象。排查顺序我固定按这三步走底部状态栏确认解释器路径记下来CtrlShiftP执行Python: Select Interpreter选同一个路径覆盖掉执行Developer: Reload Window让 Pylance 重新索引。九成的情况这三步就解决了。如果还不行检查这个包是不是装在别的地方了——比如你pip install时终端其实没激活环境。用python -m pip show requests看Location字段指向哪个目录和当前解释器的site-packages对一下。还有一种情况是包确实装了但类型信息缺失。这时候错误提示通常是reportMissingModuleSource或者类似的可以在settings.json里针对性关掉而不是把整个类型检查关掉python.analysis.diagnosticSeverityOverrides: { reportMissingModuleSource: none }5.2 断点变成空心灰点正常断点是实心红点如果变成空心灰点说明调试器没有把断点绑定到任何可执行代码上。原因有几种按出现频率排现象原因处理方式灰点鼠标悬停提示未绑定断点启动的代码和断点所在文件不是同一份路径映射问题检查program指向的文件远程场景检查pathMappings灰点提示已跳过命中了justMyCode: true的过滤改成false或把该文件加进justMyCode例外灰点代码根本没被执行到分支没走到、函数没被调用在更靠前的位置打一个断点验证流程灰点文件是编译产物断点打在.pyc或生成代码上换到源文件最常见的是第一种。我遇到过一次本地代码在~/project/src/main.py容器里挂载到了/app/main.py没配pathMappings断点全是灰的但程序在跑日志也有输出就是不停。加上路径映射立刻就正常了。5.3 中文输出乱码和带空格的路径Windows 上跑处理中文的脚本终端输出乱码是高频问题。原因通常是代码里的编码声明和终端编码不一致。比较稳的做法是在入口处显式设置import sys import io sys.stdout io.TextIOWrapper(sys.stdout.buffer, encodingutf-8) sys.stderr io.TextIOWrapper(sys.stderr.buffer, encodingutf-8)或者在launch.json的env里加上env: { PYTHONIOENCODING: utf-8 }读文件的时候同理别依赖系统默认编码open()显式带encodingutf-8。这个习惯在跨平台项目上能省掉大量时间。路径带空格的问题主要出在args和外部命令调用上。Windows 上类似C:\Program Files\...这种路径拼命令行的时候必须加引号。用subprocess的时候尽量传列表而不是字符串subprocess.run([python, -m, pip, install, path], checkTrue)传列表由解释器负责参数切分比自己拼字符串加引号可靠得多。5.4 调试器卡在正在连接不动这个问题我遇到过两次两次原因不一样。第一次是端口被占。attach 模式配置的端口上已经有别的进程在监听调试器连不上去。用netstat -ano | findstr 5678Windows或者lsof -i :5678macOS、Linux看一眼换一个端口就行。第二次是防火墙拦截。远程调试场景下本地和远程主机之间的调试端口不通。这种需要在主机侧放行端口具体怎么放行取决于你的网络环境和运维策略直接找管主机的人确认最快。还有一种不太常见但存在的情况杀毒软件把调试器的进程注入行为拦了。表现是调试会话建立起一瞬间然后断开日志里能看到连接被重置。遇到这种情况把项目目录加进白名单而不是把杀毒软件整体关掉。排查这类问题的通用思路是先降级再排查把配置简化到最小就一个单文件的没有args、没有env、没有 attach确认能停下断点再一项一项加回来。每次只加一项坏了就知道是哪一项的问题。这个笨办法比对着文档猜快得多。6. 把配置固化下来团队复用、远程与容器6.1 .vscode 目录里提交什么、忽略什么.vscode目录到底提不提交争论挺多的。我的做法是分开处理提交settings.json只放团队共识的部分别放个人偏好比如字体、主题、快捷键、launch.json常用的几个调试配置、extensions.json推荐扩展列表、tasks.json如果有统一的构建、检查任务。不提交包含本机绝对路径的任何配置、含敏感信息的env、个人的*.code-snippets。settings.json里个人偏好和团队规范混在一起会很难受。比如你习惯自动保存、别人不习惯这就不该提交但代码格式化规则必须提交否则每个人提交的 diff 都在格式化。判断标准很简单这条配置不理解的人改了会不会影响别人会就提交不会就放用户级 settings。如果确实需要同时兼顾用工作区推荐 用户级覆盖的方式。VSCode 的配置优先级是工作区高于用户级所以工作区里定义了格式化工具个人改不掉但这正是团队规范想要的效果。6.2 远程主机与容器里的解释器选择现在越来越多开发场景是代码跑在远程主机或容器里本地只是编辑器。VSCode 通过 Remote 系列扩展支持这种模式配置逻辑和本地有些差别主要是解释器的来源变了。连上远程之后Select Interpreter列表里显示的是远程主机上的 Python不是本地的。这时候本地装没装 Python 完全不影响但远程主机上必须有你要用的那个环境。常见的两个坑第一个坑是远程主机上没有虚拟环境用的是系统的那个。表现是pip install装完之后重启窗口包又没了——因为装在了一个临时的、或者共享的位置。正确做法是在远程主机上按项目建虚拟环境然后在 VSCode 里选它。第二个坑是容器重建之后解释器路径变了。容器里/usr/local/bin/python可能是软链重建后指向不同版本之前选好的解释器记录失效会弹出提示让你重新选。这个没什么好办法重新选一次就行但如果你的开发流很依赖容器重建建议在容器镜像里固定 Python 的绝对路径减少变化。远程调试还涉及一个实际问题文件同步和索引。代码量大、依赖多的时候Pylance 在远程主机上做索引会吃掉不少 CPU。如果远程主机配置不高可以在settings.json里限制索引范围python.analysis.exclude: [ **/node_modules, **/.venv, **/data ]把数据目录、环境目录排除掉索引会快很多内存占用也降下来。6.3 一份可以直接抄的最小配置清单最后把上面零散提到的东西收拢成一份清单新项目直接按这个来能覆盖大部分日常场景。第一步建环境python -m venv .venv第二步写.vscode/settings.json{ python.defaultInterpreterPath: ${workspaceFolder}/.venv/bin/python, python.terminal.activateEnvironment: true, python.analysis.typeCheckingMode: basic, python.analysis.exclude: [**/.venv, **/__pycache__], editor.formatOnSave: true, [python]: { editor.defaultFormatter: charliermarsh.ruff }, python.testing.pytestEnabled: true, python.testing.pytestArgs: [tests] }第三步写.vscode/launch.json{ version: 0.2.0, configurations: [ { name: 当前文件, type: debugpy, request: launch, program: ${file}, console: integratedTerminal, cwd: ${workspaceFolder}, justMyCode: true }, { name: pytest 当前文件, type: debugpy, request: launch, module: pytest, args: [${file}, -v], console: integratedTerminal, justMyCode: false } ] }第四步.gitignore里加.venv/ __pycache__/ .pytest_cache/ .env第五步装上扩展用Select Interpreter选.venv重载窗口然后跑一次python -c import sys; print(sys.executable)确认路径。这五步走完F5 就能正常打断点终端里pip install装的东西和编辑器认的是同一份测试面板也能用。后面遇到问题按第 5 节那几条排查链路倒查就行。我在多个项目上反复做过这套配置最大的体会是出问题的时候八成的根因都在解释器不一致这一个点上。终端一个、调试器一个、语言服务器一个三份指向不同的 Python症状千奇百怪但解法都是回到状态栏把那行路径对齐。所以每次新环境配好我第一件事就是敲python -c import sys; print(sys.executable)看一眼再说别的。这十秒钟的习惯比记住一百条配置项管用。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。